---
title: 'Önbellekleme ve Çevrimdışı Destek'
source: 'https://academia.sh/tr/kurslar/android-mimari/onbellekleme-ve-cevrimdisi-destek'
course: 'Mimari Bileşenler ve Veri Katmanı'
language: tr
updated: '2026-10-21T05:02:37+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Tek doğruluk kaynağı kalıbını arayüzün yalnız yerel veritabanını okuması olarak açıklar'
    - 'Ağın yalnız veritabanını yenilediğini ve okumanın zincirine girmediğini gösterir'
    - 'Yenileme sonrası okumanın kökenini disk olarak ölçer'
    - 'Ağ düşünce kökenin disk kaldığını, değerin eskidiğini ve diskte olmayan anahtarın varsayılana düştüğünü gösterir'
---

# Önbellekleme ve Çevrimdışı Destek

Tek doğruluk kaynağı kalıbında arayüz yalnız yerel veritabanını okur, ağ yalnız veritabanını yeniler. Ders yenileme sonrası okumanın kökenini, ağ düşünce kökenin aynı kalıp değerin eskidiğini ve diskte hiç olmayan anahtarın neye düştüğünü ölçer.

Önceki derste ağdan gelen yanıt çözüldü ve zincirde bir basamak oldu; önbellek de eski bir yanıtın çözülmüş hâliydi. Bu derste yanıtın zincirde durmasını değil, kalıcı olarak diske yazılmasını inceliyoruz. Ağ yanıtı diske yazılırsa ekranın ağa hiç bakmasına gerek kalmaz ve ağ düştüğünde de ekran çalışmaya devam eder. Dersin sorusu önceki derslerle aynıdır: **bu okumanın yanıtı nereden geliyor?**

```kotlin
// 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 }}")
    }
}
```

## Tek Doğruluk Kaynağı

Tek doğruluk kaynağı (single source of truth) kalıbı iki kural koyar. Arayüz yalnız yerel veritabanını okur; ağa hiç doğrudan bakmaz. Ağ yalnız veritabanını yeniler; arayüze doğrudan veri vermez. Veri bir yönde akar: ağdan veritabanına, veritabanından arayüze. Böylece ekran tek bir yerden beslenir ve ağ olmasa da o yer çalışır.

```kotlin
// Platform kodu: ProductRepository
class ProductRepository(private val dao: ProductDao, private val api: ProductApi) {
    fun products(): Flow<List<ProductEntity>> = dao.observeAll()

    suspend fun refresh() {
        try {
            dao.upsertAll(api.products())
        } catch (e: IOException) {
            // cevrimdisiysa eski kayitlar kalir
        }
    }
}
```

`products` yalnız veritabanını izler. `refresh` ağdan alıp veritabanına yazar ve ağ yoksa hiçbir şey yazmaz; veritabanı değişince akış yeni listeyi arayüze iletir. Bu parça platform kodudur ve burada çıktı, süre ya da kayıt sayısı iddia etmiyoruz. Model, platformun bu kuralını kurar; model platformun kendisi değildir.

Modelde yenileme, ağdan istenen anahtarları eski eşleme ekleyen bir işlevdir. Ağ yoksa eşlem değişmez. Okumanın zincirinde yalnız disk vardır; ağ hiçbir okumanın zincirine girmez.

```kotlin
// Refresh.kt — aga ulasilirsa istenen anahtarlari diske yazar, ulasilmazsa diski olduğu gibi birakir
fun refreshFrom(disk: Map<String, String>, network: Tier?, keys: List<String>): Map<String, String> {
    if (network == null) return disk
    return disk + keys.mapNotNull { key -> network.lookup(key)?.let { key to it } }
}
```

Ekran üç değer okur ve yalnız diske bakar.

```kotlin
// Screen.kt — arayuz yalniz diski okur
fun readProductScreen(disk: Map<String, String>): List<Read> {
    val chain = listOf(Tier("disk", disk))
    return listOf(
        Read("fiyat", "urun.fiyat", chain, "bilinmiyor"),
        Read("stok", "urun.stok", chain, "0"),
        Read("kampanya", "kampanya.kod", chain, "yok"),
    )
}
```

## Yenileme Diske Yazar

Diskte önceki oturumdan kalan fiyat ve stok var. Sunucu fiyatı güncellemiş. Yenilemeden önce ve sonra ekranın okuması yan yana.

```kotlin
// Main.kt — yenileme ag degerini diske yazar, okuma yine diskten gelir
fun main() {
    val disk = mapOf("urun.fiyat" to "120", "urun.stok" to "9")
    val network = Tier("ag", mapOf("urun.fiyat" to "125", "urun.stok" to "8"))
    println("yenileme oncesi")
    readProductScreen(disk).forEach { it.show() }
    val refreshed = refreshFrom(disk, network, listOf("urun.fiyat", "urun.stok", "kampanya.kod"))
    println("yenileme sonrasi")
    val reads = readProductScreen(refreshed)
    reads.forEach { it.show() }
    countOrigins(reads)
}
```

```text
yenileme oncesi
fiyat: anahtar=urun.fiyat | deger=120 -> disk
stok: anahtar=urun.stok | deger=9 -> disk
kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan
yenileme sonrasi
fiyat: anahtar=urun.fiyat | deger=125 -> disk
stok: anahtar=urun.stok | deger=8 -> disk
kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan
bellek: 0
disk: 2
ag: 0
varsayilan: 1
```

Yenilemeden sonra fiyat 125, stok 8 oldu ve köken değişmedi: hâlâ disk. Ağ bir okumanın kökeni olmadı; ağ yalnız diskin içeriğini değiştirdi. Bu dersin kökeni disktir: ekran her durumda diskten okur ve ağın etkisi yalnızca diskin değerinde görünür. Sunucu kampanya kodunu bilmediği için o anahtar diske yazılmadı ve okuma varsayılanda kaldı.

**VK1.** Model, yenilemeyi tek adımda ve bütünüyle başarılı varsayar: istenen anahtarlar ya hep birlikte yazılır ya hiç yazılmaz. Platformda yenilemenin yarıda kesilmesi, iki yenilemenin üst üste binmesi ve yazma sırasında veritabanı işleminin başarısız olması modelde yoktur.

## Ağ Düşünce

Ağ yoksa `refresh` hiçbir şey yazmaz. Eski kayıtlar kalır ve ekran onları göstermeye devam eder.

```kotlin
// Main.kt — ag dusunce okuma yine diskten gelir ama deger eskidir
fun main() {
    val disk = mapOf("urun.fiyat" to "120", "urun.stok" to "9")
    val refreshed = refreshFrom(disk, null, listOf("urun.fiyat", "urun.stok", "kampanya.kod"))
    val reads = readProductScreen(refreshed)
    reads.forEach { it.show() }
    countOrigins(reads)
}
```

```text
fiyat: anahtar=urun.fiyat | deger=120 -> disk
stok: anahtar=urun.stok | deger=9 -> disk
kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan
bellek: 0
disk: 2
ag: 0
varsayilan: 1
```

Okumanın kökeni değişmedi: fiyat ve stok disk, sayılar da yenilemeden önceki ölçümle aynı. Ağ yoksa değişen şey değerdir: fiyat 120 kaldı, sunucuda ise 125. Ekran çevrimdışı çalışır ve gösterdiği değer eskidir.

Bu ölçüm M30/K04'teki bayatlık ölçüsünden farklıdır. O kursta bir ekranın gösterdiği değerin modelin değerinden sapması yargılanıyordu: ekran ya güncel ya bayattı. Bu derste tazelik yargılanmaz; kural yalnız okumanın hangi basamaktan geldiğini söyler. İki ölçümde de fiyat 120'dir ve köken disktir; değerin eski olduğunu kural değil, yan yana koyduğumuz sunucu değeri söyler.

## Sınırlayıcı Ölçüm

Diskte hiç olmayan bir anahtar ne olur? Uygulama ilk kez çalışıyor, disk boş ve ağ yok. Bu, ağın düştüğü ölçümün en uç hâlidir: ekranın gösterebileceği eski bir değer bile yoktur.

```kotlin
// Main.kt — bos diskte ve agsiz her okuma varsayilana duser, ag gelince diske yazilir
fun main() {
    val network = Tier("ag", mapOf("urun.fiyat" to "125", "urun.stok" to "8", "kampanya.kod" to "YAZ10"))
    val keys = listOf("urun.fiyat", "urun.stok", "kampanya.kod")
    val states = listOf("agsiz" to null, "agli" to network)
    for ((name, source) in states) {
        println("ilk acilis: $name")
        val reads = readProductScreen(refreshFrom(emptyMap(), source, keys))
        reads.forEach { it.show() }
        countOrigins(reads)
    }
}
```

```text
ilk acilis: agsiz
fiyat: anahtar=urun.fiyat | deger=bilinmiyor -> varsayilan
stok: anahtar=urun.stok | deger=0 -> varsayilan
kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan
bellek: 0
disk: 0
ag: 0
varsayilan: 3
ilk acilis: agli
fiyat: anahtar=urun.fiyat | deger=125 -> disk
stok: anahtar=urun.stok | deger=8 -> disk
kampanya: anahtar=kampanya.kod | deger=YAZ10 -> disk
bellek: 0
disk: 3
ag: 0
varsayilan: 0
```

Ağsız ilk açılışta üç okumanın üçü varsayılan. Diskte olmayan anahtar için ağın düşmesi bir değer ortaya çıkarmaz; ekran yedek değerlerle açılır. Ağ geldiğinde yenileme üç anahtarı da diske yazar ve aynı okumalar bu kez diskten gelir. Tek doğruluk kaynağında ağ, okumanın kökeni olmaz; yalnız diskin dolmasını sağlar.

Ölçümün söylemediği şey, hangi değerin doğru olduğudur ve ağsız ilk açılışta ekranın nasıl görünmesi gerektiğidir. İkincisi bir tasarım kararıdır ve kural onu vermez.

**VK2.** Model, ağın ya hiç erişilemediğini ya da tüm istenen anahtarları verdiğini varsayar. Platformda kısmi yanıt, yavaş bağlantı ve ağın geri gelmesine kadar bekleyen yenilemenin ne zaman yeniden denendiği modelde yoktur.

## Özet

- Bu dersin kökeni: disk. Tek doğruluk kaynağında arayüz yalnız veritabanını okur ve ağ okumanın zincirine girmez.
- Yenileme ağdan diske yazar; yenileme sonrası fiyat 125 ve stok 8 olsa da köken disk kalır: disk 2, varsayılan 1.
- Ağ düşünce yenileme bir şey yazmaz; okuma yine disktir, ama fiyat 120 kalır ve eskidir.
- Tazelik burada yargılanmaz; kural değerin güncel olup olmadığını değil, hangi basamaktan geldiğini söyler.
- Diskte hiç olmayan anahtar ağsız ilk açılışta varsayılandır: varsayılan 3; ağ gelince disk 3 olur.

## Sonraki Adım

Bu derste ekran tek bir ürünü okudu ve disk onu tek seferde tutabildi. Liste uzadığında hepsini birden getirmek ne ağa ne belleğe uyar; veri parça parça yüklenir. Sıradaki ders, `Sayfalı Veri Yükleme`, henüz yüklenmemiş bir sayfadaki ögenin hangi kökenden okunduğunu ve sayfa yüklenince kökenin nasıl değiştiğini ölçer.
