Ders 09 / 14
Anahtar–Değer Saklama
Anahtar–değer deposu küçük tercihleri tek bir anahtarla saklar; okuma ve yazma eşzamansızdır ve değer akış olarak gelir. Ders hiç yazılmamış bir tercihin okumasının kökenini ve kullanıcı yazınca bu kökenin nasıl değiştiğini ölçer.
Yayın Güncelleme
Bu derste
- Tercih verisinin yapılı bir tabloya değil anahtar–değer deposuna uyduğunu açıklar
- Eşzamansız okuma ve yazmanın akış olarak göründüğünü ayırt eder
- Hiç yazılmamış bir tercihin okumasının kökenini varsayılan olarak ölçer
- Kullanıcı yazınca ve boş dizgi yazılınca kökenin disk olduğunu gösterir
İçindekiler
Önceki derste bir göç, tablodaki satırları yeni şemaya taşıdı. Her veri bir tabloya girecek kadar yapılı değildir: tema, dil ve bildirim açık mı gibi tercihlerin her biri tek bir anahtarla yazılan tek bir değerdir. Bu derste o küçük veriye bakıyoruz ve en çok ilgilendiğimiz an, kullanıcının daha hiçbir şey yazmadığı andır. Dersin sorusu önceki derslerle aynıdır: bu okumanın yanıtı nereden geliyor?
// Origin.kt — bir okumanin yanitinin hangi basamaktan geldigini siniflandirir
class Tier(val origin: String, private val entries: Map<String, String>) {
init {
require(origin in listOf("bellek", "disk", "ag")) { "gecersiz basamak: $origin" }
}
fun lookup(key: String): String? = entries[key]
}
class Read(val label: String, val key: String, chain: List<Tier>, fallback: String) {
private val hit: Tier? = chain.firstOrNull { it.lookup(key) != null }
val value: String = hit?.lookup(key) ?: fallback
val origin: String = hit?.origin ?: "varsayilan"
fun show() {
println("$label: anahtar=$key | deger=$value -> $origin")
}
}
fun countOrigins(reads: List<Read>) {
for (name in listOf("bellek", "disk", "ag", "varsayilan")) {
println("$name: ${reads.count { it.origin == name }}")
}
}
Tercih Verisi
Anahtar–değer deposu (key-value store), değerleri bir tabloya değil, dize anahtarlara bağlar. Şema yoktur; bir anahtar ya vardır ya yoktur. Tercih (preference) bu depoya yazılan küçük bir ayardır. Platformdaki tercih deposunun iki özelliği vardır. Birincisi, okuma ve yazmanın eşzamansız olmasıdır: ikisi de askıya alınan fonksiyondur ve ana iş parçacığını bloklamaz. İkincisi, okumanın bir kerelik değer değil, akış olarak gelmesidir: tercih her değiştiğinde yeni değer akıştan çıkar.
// Platform kodu: ThemePreferences
class ThemePreferences(private val dataStore: DataStore<Preferences>) {
private val themeKey = stringPreferencesKey("ayar.tema")
val theme: Flow<String> = dataStore.data.map { it[themeKey] ?: "acik" }
suspend fun setTheme(value: String) {
dataStore.edit { it[themeKey] = value }
}
}
theme akışı, anahtar yazılmamışsa "acik" değerini üretir. Bu satır dersin konusunun kendisidir: tercih deposu yazılmamış anahtar için bir değer vermez, onu okuyan kod ?: ile bir yedek seçer. Bu parça platform kodudur ve burada çıktı, süre ya da dosya yolu iddia etmiyoruz. Model, platformun bu kuralını kurar; model platformun kendisi değildir.
Modelde tercih deposu bir disk basamağıdır. Yazma, eski eşlemi bozmadan yeni bir eşlem veren bir işlevdir.
// Preferences.kt — yazma, eski eslemi bozmadan yeni bir esleme verir
fun write(store: Map<String, String>, key: String, value: String): Map<String, String> =
store + (key to value)
Ayarlar ekranı üç tercih okur. Her birinin kendi yedek değeri vardır: tema için açık, dil için Türkçe, bildirim için evet.
// Settings.kt — ayarlar ekraninin okudugu uc tercih
fun readSettings(disk: Tier): List<Read> {
val chain = listOf(disk)
return listOf(
Read("tema", "ayar.tema", chain, "acik"),
Read("dil", "ayar.dil", chain, "tr"),
Read("bildirim", "bildirim.acik", chain, "evet"),
)
}
Hiç Yazılmamış Tercih
Kullanıcı uygulamayı ilk kez açtı. Tercih deposunda yalnız profilin adı var; ayar anahtarlarından hiçbiri yazılmamış.
// Main.kt — hic yazilmamis tercih yedek degerden okunur
fun main() {
val store = write(emptyMap(), "profil.ad", "Ayse")
val reads = readSettings(Tier("disk", store))
reads.forEach { it.show() }
countOrigins(reads)
}
tema: anahtar=ayar.tema | deger=acik -> varsayilan dil: anahtar=ayar.dil | deger=tr -> varsayilan bildirim: anahtar=bildirim.acik | deger=evet -> varsayilan bellek: 0 disk: 0 ag: 0 varsayilan: 3
Üç okumanın üçü de varsayılana düştü. Disk basamağı sıfır eşleşme verdi, çünkü ayar anahtarlarının hiçbiri yazılmamış. Bu dersin kökeni varsayılandır: tercih deposu, yazılmadıkça yanıt vermeyen bir basamaktır.
Burada bir ayrım yapmak gerekir. Android Platformu ve Araçlar kursunda (M31/K02) platformun istenen kaynağı bulamayıp başka bir kaynak vermesi ikame diye sınıflanıyordu. Bu dersteki varsayılan o ikame değildir: orada platform başka bir değer seçiyor ve seçilen değer istenenden farklı olabiliyordu; burada hiçbir basamak anahtarı taşımıyor ve yedek değeri okuyan kod kendisi getiriyor.
VK1. Model, tercih deposunun yalnız yazılmış anahtarları taşıdığını varsayar. Platformda bir anahtarın silinmesi, tercihin farklı bir türle yazılması ve depo dosyasının bozulması modelde yoktur.
Kullanıcı Yazınca
Kullanıcı temayı koyu yapar. Yazma, eski eşlemi bozmadan yeni bir eşlem verir ve tema anahtarı artık diskte vardır.
// Main.kt — kullanici temayi yazinca ayni okuma diskten gelir
fun main() {
val store = write(write(emptyMap(), "profil.ad", "Ayse"), "ayar.tema", "koyu")
val reads = readSettings(Tier("disk", store))
reads.forEach { it.show() }
countOrigins(reads)
}
tema: anahtar=ayar.tema | deger=koyu -> disk dil: anahtar=ayar.dil | deger=tr -> varsayilan bildirim: anahtar=bildirim.acik | deger=evet -> varsayilan bellek: 0 disk: 1 ag: 0 varsayilan: 2
Tema diskten okunuyor ve değeri koyudur. Dil ve bildirim değişmedi, hâlâ varsayılandır. Okuyan kod aynı kaldı; yalnız deponun içeriği değişti.
Sınırlayıcı Ölçüm
Yazılan değer, yedek değerle aynı olursa ne olur? Ya da boş bir dizgi yazılırsa? Üç durumu aynı anda deneyelim. Tema, yedeğiyle aynı değer olan acik olarak yazılıyor. Dil, boş dizgi olarak yazılıyor. Bildirim yazılmıyor.
// Main.kt — yedekle ayni deger ve bos dizgi de yazilmis tercihtir
fun main() {
val written = write(write(emptyMap(), "ayar.tema", "acik"), "ayar.dil", "")
val reads = readSettings(Tier("disk", written))
reads.forEach { it.show() }
countOrigins(reads)
}
tema: anahtar=ayar.tema | deger=acik -> disk dil: anahtar=ayar.dil | deger= -> disk bildirim: anahtar=bildirim.acik | deger=evet -> varsayilan bellek: 0 disk: 2 ag: 0 varsayilan: 1
Tema hâlâ acik ve bu kez disktendir. Yedek değerle yazılan değer aynıydı; köken değişti, çünkü anahtar artık yazılı. Dilin değeri boş dizgi ve onun da kökeni disktir. Boş dizgi bir yanıttır, varsayılan değildir: Tier.lookup boş dizgiyi null saymaz, anahtarı taşıdığını söyler. Okuma yedek değer olan tr’ye düşmedi.
Bu ölçümün yararı, “tercih boş” ile “tercih hiç yazılmamış” ayrımını görünür kılmasıdır. Kullanıcı dili bilerek boş bıraktıysa, yedek değer o kararı ezmemelidir. Kuralın söylemediği şey, boş dizginin uygulama için anlamlı olup olmadığıdır; bunu ders değil, o tercihi kullanan kod söyler.
VK2. Model, tercihi dize olarak saklar ve boş dizgiyi geçerli bir değer sayar. Platformda tercihin sayı, mantıksal değer ya da dize türünde tanımlanması ve türe göre farklı anahtar nesnesi gerekmesi modelde yoktur.
Özet
- Bu dersin kökeni: varsayılan. Hiç yazılmamış bir tercihi hiçbir basamak taşımaz ve yedek değeri okuyan kod verir.
- Anahtar–değer deposu küçük tercihleri tek bir anahtarla saklar; platformda okuma akış, yazma askıya alınan fonksiyondur.
- Boş depoda üç okumanın üçü varsayılandır: disk 0, varsayılan 3.
- Kullanıcı bir tercihi yazınca aynı okuma disk olur: disk 1, varsayılan 2. Yedekle aynı değer yazılırsa değer aynı kalır, köken disk olur.
- Boş dizgi yazılmış bir tercihtir; okuması diskten gelir ve yedek değere düşmez.
Sonraki Adım
Bu derste disk basamağı, tek bir anahtarlı küçük bir mağazaydı. Uygulama bazen bir resim ya da belge gibi bir dosya saklar ve dosya yalnız uygulamaya ait bir dizinde ya da başka uygulamaların da gördüğü ortak bir yerde durabilir. Sıradaki ders, Dosya ve Medya Depolama, uygulamanın kendi dizinindeki dosyanın hangi kökenden okunduğunu ve başka uygulamanın dosyasının neden zincire girmediğini ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.