• 7.09.2026 21:07:29
  • Admin Admin

Jetpack Compose kullanan Android istemcilerinde OkHttp HTTP önbelleğini sadece açmak yeterli değildir. Bu yazı, Cache-Control, ETag, ölçüm, hesap geçişi ve test stratejisini uygulanabilir örneklerle ele alır.

Android Eğitimi: OkHttp HTTP Önbelleğini Ölçerek Tasarlamak

Android eğitimi kapsamında HTTP önbellek sözleşmesini kurmak

Bir Android istemcisinde disk cache davranışını OkHttp belirler, fakat hangi yanıtın ne kadar süre kullanılabileceğini sunucunun Cache-Control, ETag ve Vary başlıkları tanımlar. Örneğin ürün kataloğu için Cache-Control: public, max-age=300 istemcinin beş dakika ağ isteği yapmadan yanıtı kullanmasına izin verir. Kullanıcıya özel profil yanıtında ise sunucu, istemci kimliği veya yetkilendirme başlığına göre içerik değişiyorsa Vary: Authorization göndermelidir. Bu başlık yoksa aynı URL'nin farklı hesaplardaki yanıtı yanlış cache anahtarıyla eşleşebilir.

OkHttp cache dizinini uygulamaya açık biçimde yapılandırın. Android'in cacheDir alanı işletim sistemi tarafından alan baskısında silinebilir; bu nedenle cache'i kalıcı veri deposu veya offline veri kaynağı olarak modellemeyin. Bu ayrım, android mvvm mimarisi içinde Room'daki iş verisi ile yeniden indirilebilir HTTP temsillerini birbirine karıştırmayı önler.

val httpCache = Cache(
    directory = File(context.cacheDir, "http_cache"),
    maxSize = 20L * 1024L * 1024L
)

val okHttpClient = OkHttpClient.Builder()
    .cache(httpCache)
    .addInterceptor { chain ->
        val request = chain.request().newBuilder()
            .header("Accept", "application/json")
            .build()
        chain.proceed(request)
    }
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttpClient)
    .addConverterFactory(MoshiConverterFactory.create())
    .build()

Yaygın hata, istemci interceptor'ında her isteğe Cache-Control: max-age=86400 ekleyerek sunucunun tazelik politikasını ezmektir. Request üzerindeki bu direktif, OkHttp'in cache stratejisini değiştirebilir ve fiyat, izin veya stok gibi hızlı değişen veriyi beklenenden eski gösterebilir. İstemcide zorlayıcı cache kuralı yalnızca sürümü sabitlenmiş yardım içeriği gibi endpoint bazında ve API sahibiyle yapılan açık sözleşmeyle uygulanmalıdır.

Kotlin eğitimi için OkHttp EventListener ile cache hit ölçümü

Cache ekledikten sonra başarı ölçütü 'uygulama daha hızlı hissettiriyor' değildir. Her endpoint için ağdan gelen yanıtı, cache ile doğrulanan yanıtı ve doğrudan cache hit'i ayırın. OkHttp'te Response.cacheResponse ve Response.networkResponse alanları bu ayrımı verir: yalnız cacheResponse varsa disk cache hit, her ikisi varsa ETag ile yapılan koşullu doğrulama ve genellikle 304 sonucu söz konusudur.

class CacheMetricsInterceptor(
    private val report: (String, Long, String) -> Unit
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val startedAt = SystemClock.elapsedRealtimeNanos()
        val response = chain.proceed(chain.request())
        val source = when {
            response.cacheResponse != null && response.networkResponse == null -> "cache_hit"
            response.cacheResponse != null && response.networkResponse != null -> "validated_304_or_merge"
            else -> "network"
        }
        val elapsedMs = (SystemClock.elapsedRealtimeNanos() - startedAt) / 1_000_000
        report(chain.request().url.encodedPath, elapsedMs, source)
        return response
    }
}

Önce-sonra karşılaştırmasında Android Studio Network Inspector'ı yalnız başına kaynak sınıflandırması için kullanmayın; Inspector ağ isteğini gösterir, fakat diskten dönen saf cache hit görünmeyebilir. Yukarıdaki interceptor çıktısını endpoint etiketiyle Telemetry SDK'nıza veya debug loguna yazın. Sabit bir Wi-Fi ağında, aynı senaryoyu en az 30 kez soğuk cache ve 30 kez sıcak cache ile çalıştırın; p50, p95 ve cache_hit / toplam oranını ayrı hesaplayın. Soğuk cache için uygulamanın kendi tanılama ekranından okHttpClient.cache?.evictAll() çağırın; pm clear kullanmak oturum ve veri tabanını da silerek deneyi bozar.

Bağlantı kurulumu, DNS ve TLS sürelerini ayırmak gerektiğinde OkHttp EventListener ile dnsStart, connectEnd ve responseHeadersEnd olaylarını kaydedin. Bu sayede p95 artışının cache politikasından mı, yoksa yeni bir DNS sağlayıcısından mı kaynaklandığını ayırabilirsiniz. Kotlin kursu veya ekip içi kotlin eğitimi materyalinde bu ayrım özellikle önemlidir: tek bir toplam süre metriği, kök nedeni göstermez.

Jetpack Compose ve android MVVM mimarisi ile gereksiz istekleri kesmek

Jetpack Compose tarafında cache aktif olsa bile composable gövdesinden repository çağırmak, recomposition sırasında aynı endpoint'e tekrar tekrar gitmeye yol açabilir. Cache hit olsa dahi JSON ayrıştırma, ana iş parçacığına sonuç taşıma ve state güncelleme maliyeti oluşur. Ağ tetiklemesini ViewModel'de tekilleştirin, UI'ya StateFlow yayınlayın ve Compose'da lifecycle-aware toplayın.

class CatalogViewModel(
    private val repository: CatalogRepository
) : ViewModel() {
    val uiState: StateFlow<CatalogUiState> = repository.catalog()
        .map { CatalogUiState.Content(it) as CatalogUiState }
        .onStart { emit(CatalogUiState.Loading) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = CatalogUiState.Loading
        )
}

@Composable
fun CatalogRoute(viewModel: CatalogViewModel) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    CatalogScreen(state)
}

SharingStarted.WhileSubscribed(5_000) ekran döndürme veya kısa süreli navigation geçişinde upstream Flow'un hemen iptal edilmesini önler. Ancak bu süre içinde kullanıcı hesabı değişebiliyorsa repository akışını sadece ekran adına göre değil, hesap kimliğiyle de anahtarlayın. Aksi halde eski hesaptan başlayan istek yeni hesaptaki ekrana sonuç yazabilir. Bu, android mvvm mimarisi içinde çoğu zaman 'cache bug' gibi görünen ama aslında iptal ve kimlik bağlamı hatası olan bir edge case'tir.

Android Studio eğitimi oturumlarında Layout Inspector yerine Compose recomposition sayaçları ve Network Inspector birlikte kullanılabilir: aynı filtre değişiminde kaç recomposition ve kaç HTTP çağrısı oluştuğunu kaydedin. Beklenen model, filtre state'i değiştiğinde tek repository isteği ve yalnız ilgili UI dalının yeniden çizilmesidir. Birden fazla çağrı görülürse LaunchedEffect(Unit) yerine filtre değerini key yapmak veya ViewModel'deki yinelenen yüklemeyi flatMapLatest ile iptal etmek gerekir.

Android kursu projelerinde ETag ve Vary için entegrasyon testi

HTTP cache davranışını sadece emulator üzerinde elle doğrulamak kırılgandır. OkHttp'nin MockWebServer aracıyla iki isteğin koşullu doğrulama yaptığını test edin. İlk yanıtta max-age=0 kullanmak, ikinci istekte cache'teki temsili bayat kabul ettirir; istemci If-None-Match ile doğrulama yapar. Sunucu 304 dönerse OkHttp gövdeyi cache'ten birleştirir.

@Test
fun `stale response is revalidated with etag`() {
    val server = MockWebServer()
    server.enqueue(
        MockResponse()
            .setResponseCode(200)
            .setHeader("ETag", "catalog-v7")
            .setHeader("Cache-Control", "max-age=0")
            .setBody("{\"items\":[1]}")
    )
    server.enqueue(MockResponse().setResponseCode(304))
    server.start()

    val cache = Cache(createTempDir(), 1L shl 20)
    val client = OkHttpClient.Builder().cache(cache).build()
    val request = Request.Builder().url(server.url("/catalog")).build()

    client.newCall(request).execute().close()
    client.newCall(request).execute().use { response ->
        assertEquals(200, response.code)
        assertNotNull(response.cacheResponse)
    }
    assertEquals("catalog-v7", server.takeRequest().getHeader("If-None-Match"))
    server.shutdown()
}

Bu testteki incelik, takeRequest() çağrısının ilk isteği tüketmesidir. Gerçek üretim testinde önce ilk request'i okuyun, sonra ikinci request üzerinde If-None-Match doğrulayın; aksi halde yanlış isteğin başlığını denetlersiniz. Ayrıca hesap değişiminde cache dizinini temizleyin veya hesap kimliğinin SHA-256 özetini dizin ayracı olarak kullanın. Ham e-posta ya da kullanıcı ID'sini dosya yoluna koymak, hata raporları ve cihaz yedeklerinde kişisel veri sızıntısı oluşturabilir.

Mobil uygulama geliştirme eğitimi ve Play Store yayınlama öncesi kontrol listesi

Mobil uygulama geliştirme eğitimi içinde release kontrolünü ölçülebilir hale getirin: debug ve release build'lerde aynı API çağrısının header'larını Charles Proxy veya mitmproxy ile karşılaştırın, ardından gerçek cihazda interceptor metriklerinden cache hit oranını alın. Release variant'ında sertifika pinning veya ağ güvenlik yapılandırması proxy trafiğini engelleyebilir; bu durumda sadece debug build'e güvenlik açısından sınırlı bir debug network security config ekleyin, release paketine eklemeyin.

Play Store yayınlama öncesinde test hesabından çıkış yapın, uygulamayı zorla durdurun, yeniden açın ve önceki hesaba ait profil, sipariş veya mesaj gövdesinin görünmediğini otomatik UI testiyle doğrulayın. Cache'i logout anında silmek için okHttpClient.cache?.evictAll() çağrısı tek başına yeterli olmayabilir; Room, DataStore ve görsel yükleyici cache'leri de aynı oturum sınırında temizlenmelidir. Bu kontrol, HTTP Vary başlığındaki bir sunucu hatasına karşı istemci tarafında ikinci savunma katmanı sağlar.

Sık Sorulan Sorular

Android eğitimi sırasında OkHttp cache hit ile 304 yanıtı nasıl ayırt edilir?

Response.cacheResponse dolu ve networkResponse null ise yanıt doğrudan disk cache'ten gelmiştir. İki alan da doluysa OkHttp ağda doğrulama yapmış, genellikle ETag ve 304 üzerinden cache gövdesini birleştirmiştir. Bu iki durumu ayrı telemetry etiketi olarak kaydedin.

Jetpack Compose ekranında aynı API isteği neden birden fazla kez gider?

Repository çağrısı composable gövdesinde yapılıyorsa her recomposition tetikleyebilir. Çağrıyı ViewModel içindeki StateFlow'a taşıyın, UI'da collectAsStateWithLifecycle kullanın ve filtre gibi değişken istek parametrelerini flatMapLatest ile yönetin.

Android Studio eğitimi için HTTP cache performansını hangi araçlarla ölçmeliyim?

İstek zaman çizelgesi için Android Studio Network Inspector, DNS-TLS-bağlantı evreleri için OkHttp EventListener, cache kaynağı için Response.cacheResponse/networkResponse metriklerini kullanın. En az 30 soğuk ve 30 sıcak cache koşusunda endpoint bazlı p50 ve p95 hesaplayın.

Play Store yayınlama öncesi kullanıcı değiştirme cache testi nasıl yapılır?

Hesap A ile kullanıcıya özel endpoint'i açın, logout yapın, HTTP cache ile Room ve görsel cache'i temizleyin, Hesap B ile aynı rotayı açın. Espresso veya Compose UI testiyle Hesap A'ya ait metinlerin görünmediğini doğrulayın; sunucuda da Vary: Authorization başlığını denetleyin.

AI / LLM Discovery

Bu makale Opendart Akademi Android eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt