Ders 11 / 15
Kaynak Sistemi
Kaynak kimliğinin yapılandırmaya göre niteleyicili dizinlerden seçilmesinin, tam eşleşmenin, varsayılana düşmenin ve niteleyici önceliğinin modeli.
Yayın Güncelleme
Bu derste
- Kaynak türlerini ve kaynak kimliğinin koddan nasıl kullanıldığını açıklar
- Niteleyicili dizinde tam eşleşmenin tam, varsayılan dizine düşmenin ikame olduğunu ölçer
- Varsayılan dizinde de bulunmayan kaynağın karşılıksız kaldığını gösterir
- İki niteleyici çakıştığında önceliğin sonucu belirlediğini sınırlayıcı ölçümle doğrular
İçindekiler
Önceki ders bildirim dosyasının birleştirmesini, her öğeyi ayrı bir istek sayarak ölçtü. Bildirim dosyası uygulamanın ne olduğunu bildirir; ekranda görünen metin, görsel ve düzen ise kaynaklarda durur. Kaynaklar kodun içine gömülmez, ayrı dosyalarda tutulur, çünkü aynı kaynak aygıtın diline, ekran boyutuna ve gece ya da gündüz görünümüne göre farklı bir dosyadan gelmelidir. Bu ders kaynak sisteminin istenen yapılandırma için bir kaynak seçerken ne verdiğini ölçer.
Kursun ölçü ekseni değişmez: bu isteği platform nasıl karşılıyor. Karşılama dörttür: tam, ikame, bekleyen ve karşılıksız. Karşılamayı etiket değil kural verir: verilen değer istenene eşitse tam, eşit değilse ikame; bekleyen ve karşılıksız doğrudan sonuçtan çıkar. Aşağıdaki dosya o kuralı taşır ve her derste aynen yinelenir. Açılış dersindeki 3/2/2/1 sayısı burada değiştirilmez.
// Fulfillment.kt — bir istegin platformca nasil karsilandigini siniflandirir
sealed interface Outcome {
data class Given(val value: String) : Outcome
data class Waiting(val on: String) : Outcome
data class Refused(val reason: String) : Outcome
}
class Ask(val label: String, val wanted: String, val outcome: Outcome) {
val fulfillment: String = when (outcome) {
is Outcome.Waiting -> "bekleyen"
is Outcome.Refused -> "karsiliksiz"
is Outcome.Given -> if (outcome.value == wanted) "tam" else "ikame"
}
fun report() {
val detail = when (outcome) {
is Outcome.Given -> "verilen=${outcome.value}"
is Outcome.Waiting -> "bekledigi=${outcome.on}"
is Outcome.Refused -> "neden=${outcome.reason}"
}
println("$label: istenen=$wanted | $detail -> $fulfillment")
}
}
fun tally(asks: List<Ask>) {
for (name in listOf("tam", "ikame", "bekleyen", "karsiliksiz")) {
println("$name: ${asks.count { it.fulfillment == name }}")
}
}
Kaynak Türleri ve Kaynak Kimliği
Kaynaklar res dizininin altında türe göre ayrılmış alt dizinlerde durur. Metinler values dizinindeki XML dosyalarındadır; çizilebilir görseller drawable dizinindedir; ekran düzenleri layout dizinindedir. Her kaynağın bir adı vardır ve derleme adımı bu adlardan kaynak kimliği (resource ID) üretir: koddan R.string.title ya da R.drawable.logo yazılarak ulaşılır. Kod hangi dosyadan okunacağını bilmez, yalnız kimliği bilir; dosyayı seçmek sistemin işidir.
<resources> <string name="title">Notes</string> <string name="subtitle">Everything in one place</string> </resources>
Bu parça varsayılan values dizinindeki metin dosyasıdır. Almanca için aynı adlarla ayrı bir dosya values-de dizinine konabilir ve yalnız title kimliğini içerebilir. Dizin adındaki -de kısmına niteleyici (qualifier) denir. Aşağıdaki parça koddan kimlikle okumayı gösterir.
// Platform kodu: kimlikle metin okuma val heading = getString(R.string.title)
Platform kodudur ve çalıştırılmaz. Sistem bu satırda, aygıtın o andaki yapılandırmasına uyan dizini seçer; model, platformun bu kuralını kurar. Niteleyiciler dil, bölge, gece görünümü ve en küçük ekran genişliği gibi bir yapılandırma özelliğini adlandırır: values-de, values-de-rAT, values-night, layout-sw600dp.
Seçim Modeli
Model beş şey bilir: kaynak anahtarı, istenen dizin adı, aygıtın yapılandırması ve dizinler. İstenen, yapılandırmanın tam adlandırdığı dizindir. Bir dizin yalnız anahtarı içeriyorsa ve niteleyicilerinin hepsi yapılandırmada varsa aday olur. Adaylar arasından önceliği en yüksek niteleyiciye sahip olan seçilir; öncelik sırası dil, bölge, ekran genişliği, gece görünümüdür. Varsayılan dizinin niteleyicisi yoktur, bu yüzden anahtarı içeriyorsa her zaman adaydır. Aday yoksa kaynak bulunamaz.
// Resources.kt — kaynak secimi modeli
class ResourceDir(val name: String, val keys: Set<String>) {
val tokens: List<String> = name.split("-").drop(1)
}
fun qualifierWeight(token: String): Int = when {
token == "night" -> 1
token.startsWith("sw") -> 2
token.length == 3 && token.startsWith("r") -> 4
else -> 8
}
fun resolveResource(key: String, wanted: String, config: Set<String>, dirs: List<ResourceDir>): Ask {
val candidates = dirs.filter { key in it.keys && it.tokens.all { token -> token in config } }
val best = candidates.maxByOrNull { dir -> dir.tokens.sumOf { qualifierWeight(it) } }
val outcome = if (best == null) Outcome.Refused("varsayilan-yok") else Outcome.Given(best.name)
return Ask(key, wanted, outcome)
}
Ağırlıklar öncelik sırasını kurar: dil ağırlığı sekiz, bölge dört, genişlik iki, gece birdir. Bir üst niteleyicinin ağırlığı altındakilerin toplamından büyüktür; bu yüzden dil, ne kadar genişlik ve gece niteleyicisi olursa olsun önce gelir. İlk ölçüm üç kaynak ister. Metin anahtarı Almanca dizinde vardır ve yapılandırma Almancadır. Görsel gece dizininde vardır ve yapılandırma geceyi içerir. Alt başlık metni ise yalnız varsayılan dizinde bulunur; Almanca dizin onu içermez.
// Main.kt — tam eslesme ve varsayilana dusme
fun main() {
val dirs = listOf(
ResourceDir("values", setOf("string/title", "string/subtitle")),
ResourceDir("values-de", setOf("string/title")),
ResourceDir("drawable", setOf("drawable/logo")),
ResourceDir("drawable-night", setOf("drawable/logo")),
)
val config = setOf("de", "night")
val asks = listOf(
resolveResource("string/title", "values-de", config, dirs),
resolveResource("drawable/logo", "drawable-night", config, dirs),
resolveResource("string/subtitle", "values-de", config, dirs),
)
for (ask in asks) ask.report()
tally(asks)
}
string/title: istenen=values-de | verilen=values-de -> tam drawable/logo: istenen=drawable-night | verilen=drawable-night -> tam string/subtitle: istenen=values-de | verilen=values -> ikame tam: 2 ikame: 1 bekleyen: 0 karsiliksiz: 0
İlk iki kimlik tam eşleşen dizinden geldi. Alt başlık için istenen dizin Almancaydı ama orada yoktu; sistem varsayılan dizindeki metni verdi. Bu ikamedir ve uygulama çökmedi: varsayılan dizin bir güvenlik ağıdır.
AP1. Model dört niteleyici türünü bilir ve her dizinde bu türlerden en çok birer tane bekler. Gerçek niteleyici kümesi çok daha geniştir ve her türün kendi eşleşme kuralı vardır.
AP2. Model, bir niteleyicinin yapılandırmada bulunmasını yeterli sayar. Ekran genişliği gibi sayısal niteleyicilerde gerçek sistem eşitlik değil eşik karşılaştırır; bu ölçümde sw600dp yalnız bir ad olarak ele alınır.
Yapılandırma Değişikliği
Aygıt döndürüldüğünde ya da gece görünümü açıldığında yapılandırma değişir ve activity yeniden kurulur; kaynaklar yeni yapılandırmaya göre yeniden seçilir. Aşağıdaki ölçüm aynı anahtarı iki yapılandırmada sorar. Dizinler values, values-de ve values-night’tır. Almanca yapılandırmada values-de seçilir. Gece eklenince hem values-de hem values-night aday olur; istenen ise ikisinin birleşimini adlandıran values-de-night’tır. Üçüncü satır yalnız Almanca dizinde olan bir anahtarı Fransızca yapılandırmada sorar.
// Main.kt — yapilandirma degisince yeniden secim
fun main() {
val dirs = listOf(
ResourceDir("values", setOf("string/title")),
ResourceDir("values-de", setOf("string/title", "string/promo")),
ResourceDir("values-night", setOf("string/title")),
)
val asks = listOf(
resolveResource("string/title", "values-de", setOf("de"), dirs),
resolveResource("string/title", "values-de-night", setOf("de", "night"), dirs),
resolveResource("string/promo", "values-fr", setOf("fr"), dirs),
)
for (ask in asks) ask.report()
tally(asks)
}
string/title: istenen=values-de | verilen=values-de -> tam string/title: istenen=values-de-night | verilen=values-de -> ikame string/promo: istenen=values-fr | neden=varsayilan-yok -> karsiliksiz tam: 1 ikame: 1 bekleyen: 0 karsiliksiz: 1
Aynı kimlik yapılandırma değişince tamdan ikameye geçti: ikinci yapılandırmanın adlandırdığı birleşik dizin yoktu. Üçüncü kimlik karşılıksızdır, çünkü string/promo yalnız Almanca dizinde vardı ve varsayılan dizinde bulunmuyordu; Fransızca yapılandırmada aday kalmadı. Gerçek sistemin bu durumda tam olarak ne yaptığı bu derste iddia edilmez; model yalnız karşılığın verilmediğini sınıflar.
Yapılandırma ölçünün girdisidir; ama sonucu belirleyen girdi değildir. Aynı yapılandırma bir kimlikte tam, ötekinde ikame verir. Sonucu belirleyen, verilen dizinin istenen dizine eşit olup olmadığıdır.
Tür Dizinleri ve Ekran Genişliği
Kaynak dizinleri türe göre adlandırılır. values metin, renk ve ölçü gibi basit değerleri, drawable görselleri, layout ekran düzenlerini taşır. Her türün dizini kendi niteleyicilerini alabilir. Ekran genişliği niteleyicisi sw600dp gibi yazılır ve en küçük kenar uzunluğunun o değerden az olmadığı aygıtlarda geçerlidir. Aşağıdaki parça geniş ekranlar için ayrı bir düzen dizinini gösterir.
<!-- layout-sw600dp/main.xml --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" />
Aynı ad layout dizininde de bulunabilir; geniş ekranda layout-sw600dp dizinindeki seçilir, dar ekranda varsayılan dizindeki. İstenen yine yapılandırmanın tam adlandırdığı dizindir. Ölçümde yapılandırma bir tablet gibi geniş ve Almanca. Üç tür ister: düzen, metin ve görsel. Düzen yalnız geniş ekran dizininde ve varsayılanda vardır. Metin yalnız Almanca dizinde ve varsayılanda vardır. Görsel yalnız varsayılan dizindedir.
// Main.kt — kaynak turleri
fun main() {
val dirs = listOf(
ResourceDir("layout", setOf("layout/main")),
ResourceDir("layout-sw600dp", setOf("layout/main")),
ResourceDir("values", setOf("string/title")),
ResourceDir("values-de", setOf("string/title")),
ResourceDir("drawable", setOf("drawable/logo")),
)
val config = setOf("de", "sw600dp")
val asks = listOf(
resolveResource("layout/main", "layout-sw600dp", config, dirs),
resolveResource("string/title", "values-de", config, dirs),
resolveResource("drawable/logo", "drawable-sw600dp", config, dirs),
)
for (ask in asks) ask.report()
tally(asks)
}
layout/main: istenen=layout-sw600dp | verilen=layout-sw600dp -> tam string/title: istenen=values-de | verilen=values-de -> tam drawable/logo: istenen=drawable-sw600dp | verilen=drawable -> ikame tam: 2 ikame: 1 bekleyen: 0 karsiliksiz: 0
İlk iki istek yapılandırmanın adlandırdığı dizinden karşılandı. Üçüncüsü geniş ekrana özel bir görsel istedi ama yalnız varsayılan görsel vardı; varsayılan verildi ve ikame oldu.
AP3. Model yalnız de, rAT, night ve sw niteleyicilerini tanır. Gerçek sistemde yoğunluk, yön ve platform sürümü gibi başka niteleyiciler de vardır; ölçüm bunları kapsamaz.
Elemenin Adımları
Seçim iki adımda yapılır. Önce yapılandırmayla çelişen dizinler elenir: bir niteleyicisi yapılandırmada olmayan dizin aday olamaz. Sonra kalan adaylar arasından niteleyici öncelik sırasına göre en iyisi seçilir. Birinci adımda values-night gündüz yapılandırmasında elenir, values-de ise Almanca yapılandırmada kalır. Aday listesi boşsa kaynak bulunamaz. Varsayılan dizinde de yoksa bu, kaynağın hiçbir yerde tanımlanmadığı anlamına gelir. Ölçümde yapılandırma gündüz ve Almanca. Gece dizini ve Almanca dizini vardır; ayrıca hiçbir dizinde olmayan bir anahtar sorulur.
// Main.kt — elenen dizin ve eksik anahtar
fun main() {
val dirs = listOf(
ResourceDir("values", setOf("string/title")),
ResourceDir("values-night", setOf("string/title")),
ResourceDir("values-de", setOf("string/title")),
)
val config = setOf("de")
val asks = listOf(
resolveResource("string/title", "values-night", config, dirs),
resolveResource("string/title", "values-de", config, dirs),
resolveResource("string/missing", "values", config, dirs),
)
for (ask in asks) ask.report()
tally(asks)
}
string/title: istenen=values-night | verilen=values-de -> ikame string/title: istenen=values-de | verilen=values-de -> tam string/missing: istenen=values | neden=varsayilan-yok -> karsiliksiz tam: 1 ikame: 1 bekleyen: 0 karsiliksiz: 1
Gece dizini istendiği halde gündüz yapılandırmasında elendi ve Almanca dizin verildi: ikame. Hiçbir dizinde olmayan anahtar karşılıksız kaldı; neden varsayilan-yok olarak adlandırılır, çünkü model varsayılan dizinde de bulamamıştır.
AP4. Model elemeyi niteleyicinin yapılandırmada bulunup bulunmadığına bakarak yapar. Gerçek sistem niteleyicileri öncelik sırasıyla tek tek eleyerek ilerler; ikisi çoğu durumda aynı sonucu verir ama ölçüm bunu karşılaştırmaz.
Sınırlayıcı Ölçüm
Model niteleyicileri tek tek eşleştirir; bir niteleyicinin yakın bir başkasıyla örtüşmesini bilmez. Almanca konuşulan her bölge için ayrı bir values-de-rAT gibi dizin yazılabilir. Bölge dizini yoksa ve yalnız values-de varsa, Avusturya bölgesi yapılandırması Almanca dizinden okur: model bunu ikame olarak sınıflar. İkinci ve üçüncü satır iki niteleyicinin çakışmasını sınar. Dizinler values-de ve values-night’tır, yapılandırma ikisini de içerir. Önce ikisi de varken, sonra Almanca dizin çıkarılınca sorulur.
// Main.kt — bolge ve oncelik
fun main() {
val region = listOf(
ResourceDir("values", setOf("string/title")),
ResourceDir("values-de", setOf("string/title")),
)
val both = listOf(
ResourceDir("values-de", setOf("string/title")),
ResourceDir("values-night", setOf("string/title")),
ResourceDir("values", setOf("string/title")),
)
val nightOnly = both.filter { it.name != "values-de" }
val config = setOf("de", "rAT", "night")
val asks = listOf(
resolveResource("string/title", "values-de-rAT", config, region),
resolveResource("string/title", "values-de-night", config, both),
resolveResource("string/title", "values-de-night", config, nightOnly),
)
for (ask in asks) ask.report()
tally(asks)
}
string/title: istenen=values-de-rAT | verilen=values-de -> ikame string/title: istenen=values-de-night | verilen=values-de -> ikame string/title: istenen=values-de-night | verilen=values-night -> ikame tam: 0 ikame: 3 bekleyen: 0 karsiliksiz: 0
Üç satır da ikamedir ve kural aralarını ayırmaz: üçünde de verilen istenenden farklıdır. Ayrımı verilen sütunu gösterir. İkinci satırda iki aday varken dil kazandı. Üçüncü satırda dil dizini kalmadığı için gece dizini seçildi. Öncelik sırasını model kurar; kural yalnız sonucu görür, o sonucun neden seçildiğini görmez. Sayım üç ikameyi sayar ve önceliğin işlediğini göstermez.
AP5. Model önceliği dil, bölge, genişlik, gece biçiminde kurar; bu dört niteleyici platformun listesinde de bu sıradadır. Platformun tam öncelik listesi daha uzundur ve ağırlık toplamı kullanmaz, niteleyicileri sırayla eler. Ölçüm yalnız bu dört niteleyici için sıranın çalıştığını gösterir.
Özet
- Bu dersin ölçütü: karşılama; verilen dizinin istenen dizine eşit olup olmadığı, yapılandırmanın kendisi değil. Bu dersin karşılaması: ikame.
- Kaynaklar
resaltında türe göre dizinlerde durur; koddan kaynak kimliğiyle ulaşılır ve dosyayı sistem seçer. - Niteleyiciler dizin adının sonundaki yapılandırma özellikleridir: dil, bölge, gece görünümü, ekran genişliği.
- Yapılandırmanın adlandırdığı dizin varsa kaynak tam, yoksa varsayılan dizinden gelen kaynak ikamedir.
- Varsayılan dizinde de bulunmayan kaynak karşılıksızdır.
- İki niteleyici çakışınca öncelik sırası sonucu belirler; kural bu nedeni görmez, yalnız verilenle istenenin eşitliğini görür.
Sonraki Adım
Kaynaklar ve bildirim dosyası bir araya gelip çalıştırılabilir bir uygulamaya dönüşmek için derlenmelidir. Sonraki ders derleme sistemini ele alır ve bağımlılıkların sürümleri çakıştığında hangi sürümün verildiğini ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.