Caching & offline-first strategies
Caching strategies sit behind the interview question "how does your app behave on a weak connection?":
- Cache-then-network (stale-while-revalidate) — show cache first, refresh in parallel, replace on arrival. Best UX; with a staleness indicator on a balance screen.
- Network-first, cache fallback — network first, cache if it fails. Where freshness is critical.
- Cache-only / Network-only — special cases (offline mode / payment operations).
Cache by layer:
- HTTP cache (OkHttp) — driven by
Cache-Controlheaders; needs server cooperation. - In-memory cache — session-long, fastest.
- DB cache (Room) — the backbone of offline-first: the DB is the single source of truth, the network updates the DB.
Invalidation is the hard part: TTL (time-based), event-based (the user made a transfer → the balance cache is stale), version-based (ETag/If-None-Match → 304).
class TransactionRepository @Inject constructor(
private val api: TransactionApi,
private val dao: TransactionDao
) {
// UI həmişə DB-yə baxır — offline-da da işləyir
fun observeTransactions(accountId: String): Flow<List<Transaction>> =
dao.observeByAccount(accountId).map { list -> list.map { it.toDomain() } }
// Yeniləmə DB-ni yazır; Flow avtomatik yeni datanı ötürür
suspend fun refresh(accountId: String): RefreshResult = try {
val remote = api.getTransactions(accountId)
dao.upsertAll(remote.map { it.toEntity() })
RefreshResult.Success
} catch (e: IOException) {
RefreshResult.Offline // UI: "son yenilənmə 14:32" göstərir
}
}An offline-first repository: read the DB, refresh from network
Banking boundaries: balances and transaction history get cached (with staleness indicators), but a payment is never shown "successful" from cache and never auto-queued offline — re-execution without user confirmation risks double charges. Sensitive responses should carry Cache-Control: no-store from the server; if they don't, enforce it with an interceptor.
🛠 Practice task
Combine the previous two tasks: write an offline-first repository with Retrofit + Room.
observeX()reads only from the DB;refresh()fetches from the network into the DB.- Turn on airplane mode and restart the app — the data must still appear.
- Add a "last updated HH:mm" indicator to the UI, warning when it is older than 5 minutes.
Done when: the screen is never blank offline and the user can see the data is stale.
📚 Sources and documentation
- Build an offline-first appofficialdeveloper.android.com
- OkHttp cachingofficiallysine.dev