Android Academy · Pelajaran

RemoteMediator dan Caching

Gabungkan paging jaringan dan basis data.

Pelajaran 4 dari 413 langkah

RemoteMediator dan Caching adalah pelajaran Android Academy gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Android Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Android Academy mencakup 4 pelajaran total.

Paging Offline-First

PagingSource jaringan biasa dapat digunakan, tetapi jika koneksi terputus, daftar menjadi kosong. Untuk aplikasi yang tangguh, Anda memerlukan basis data sebagai satu-satunya sumber kebenaran, sementara jaringan mengisinya di latar belakang.

Itulah yang dimungkinkan oleh RemoteMediator: memuat halaman dari basis data lokal dan mengambil data tambahan dari jaringan saat basis data kehabisan data.

Arsitekturnya

Dengan RemoteMediator, data mengalir dalam satu arah:

  • Antarmuka pengguna memuat halaman dari PagingSource Room
  • Saat Room hampir mencapai akhir datanya, RemoteMediator mengambil halaman jaringan berikutnya
  • Hasil dari jaringan ditulis ke Room
  • Room memancarkan baris baru, lalu antarmuka pengguna diperbarui

Pengguna selalu langsung melihat data yang tersimpan di cache, bahkan saat offline.

// Network ---> RemoteMediator ---> Room (source of truth) ---> UI
// UI never reads the network directly

PagingSource Room Secara Otomatis

Room dapat membuat PagingSource secara otomatis. Cukup deklarasikan kueri yang mengembalikan PagingSource<Int, Entity>, lalu Room akan mengimplementasikannya.

Inilah sumber data yang benar-benar digunakan antarmuka pengguna untuk memuat halaman.

import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Query

@Dao
interface ArticleDao {
    @Query("SELECT * FROM articles ORDER BY position ASC")
    fun pagingSource(): PagingSource<Int, ArticleEntity>
}

Membuat Subkelas RemoteMediator

RemoteMediator<Key, Value> memiliki satu metode wajib, yaitu load(), serta satu initialize() opsional.

load() menerima LoadType (REFRESH, PREPEND, atau APPEND) dan PagingState saat ini, lalu mengembalikan MediatorResult.

import androidx.paging.ExperimentalPagingApi
import androidx.paging.RemoteMediator

@OptIn(ExperimentalPagingApi::class)
class ArticleRemoteMediator(
    private val api: ArticleApi,
    private val db: AppDatabase
) : RemoteMediator<Int, ArticleEntity>() {
    // override suspend fun load(loadType, state): MediatorResult
}

Menangani LoadType

Di dalam load(), Anda menentukan halaman yang akan diambil berdasarkan LoadType:

  • REFRESH - memuat halaman pertama (atau halaman di sekitar jangkar)
  • PREPEND - biasanya tidak ada yang perlu dilakukan; kembalikan endOfPaginationReached = true
  • APPEND - memuat halaman setelah halaman terakhir yang tersedia
val page: Int = when (loadType) {
    LoadType.REFRESH -> 1
    LoadType.PREPEND ->
        return MediatorResult.Success(endOfPaginationReached = true)
    LoadType.APPEND -> {
        val lastKey = db.remoteKeyDao().last()?.nextKey
            ?: return MediatorResult.Success(endOfPaginationReached = true)
        lastKey
    }
}

Tabel Kunci Jarak Jauh

Berbeda dari PagingSource, RemoteMediator tidak dapat mengembalikan kunci berikutnya/sebelumnya secara langsung—kunci tersebut harus disimpan. Simpan kunci itu dalam tabel kunci jarak jauh kecil bersama data Anda.

Dengan begitu, Anda dapat melanjutkan APPEND/PREPEND setelah aplikasi dimulai ulang.

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "remote_keys")
data class RemoteKey(
    @PrimaryKey val articleId: Int,
    val prevKey: Int?,
    val nextKey: Int?
)

Menulis Hasil Jaringan ke Room

Setelah mengambil sebuah halaman, tulis item dan kuncinya ke Room di dalam transaksi. Saat REFRESH, hapus tabel lama terlebih dahulu agar cache tetap konsisten.

db.withTransaction {
    if (loadType == LoadType.REFRESH) {
        db.remoteKeyDao().clearAll()
        db.articleDao().clearAll()
    }
    val nextKey = if (response.items.isEmpty()) null else page + 1
    db.remoteKeyDao().insertAll(
        response.items.map { RemoteKey(it.id, prevKey = page - 1, nextKey = nextKey) }
    )
    db.articleDao().insertAll(response.items.map { it.toEntity() })
}

Mengembalikan MediatorResult

Akhiri load() dengan mengembalikan MediatorResult:

  • Success(endOfPaginationReached = true) saat tidak ada halaman lain
  • Success(endOfPaginationReached = false) saat mungkin masih ada halaman lain
  • Error(e) jika terjadi kegagalan—ditampilkan sebagai LoadState.Error
return try {
    val response = api.getArticles(page = page, size = state.config.pageSize)
    db.withTransaction { /* write items + keys */ }
    MediatorResult.Success(
        endOfPaginationReached = response.items.isEmpty()
    )
} catch (e: IOException) {
    MediatorResult.Error(e)
} catch (e: HttpException) {
    MediatorResult.Error(e)
}

Menghubungkan Pager dengan remoteMediator

Buat Pager dengan remoteMediator dan pagingSourceFactory Room. Mediator mengisi Room, sedangkan antarmuka pengguna memuat halaman dari Room.

Karena menggunakan API eksperimental, aktifkan opt-in dengan @OptIn(ExperimentalPagingApi::class).

@OptIn(ExperimentalPagingApi::class)
fun articles(): Flow<PagingData<ArticleEntity>> = Pager(
    config = PagingConfig(pageSize = 20),
    remoteMediator = ArticleRemoteMediator(api, db),
    pagingSourceFactory = { db.articleDao().pagingSource() }
).flow

Mencari Kunci untuk APPEND

Untuk pemuatan APPEND, Anda memerlukan nextKey dari item terakhir yang dimuat. Gunakan jangkar PagingState untuk menemukan kunci jarak jauh yang tepat dari tabel kunci Anda.

Fungsi pembantu membuat load() tetap mudah dibaca untuk REFRESH, APPEND, dan PREPEND.

private suspend fun lastRemoteKey(
    state: PagingState<Int, ArticleEntity>
): RemoteKey? {
    return state.pages.lastOrNull { it.data.isNotEmpty() }
        ?.data?.lastOrNull()
        ?.let { db.remoteKeyDao().keyFor(it.id) }
}

// In load(): for APPEND
val nextKey = lastRemoteKey(state)?.nextKey
    ?: return MediatorResult.Success(endOfPaginationReached = true)

Caching dan Kesegaran Data

Karena data berada di Room, aplikasi dapat dimuat secara instan dan bekerja secara offline. Untuk menghindari konten yang sudah usang, ganti initialize() untuk menentukan apakah perlu melakukan refresh saat aplikasi diluncurkan, berdasarkan usia cache.

override suspend fun initialize(): InitializeAction {
    val lastUpdate = db.remoteKeyDao().lastUpdatedMillis() ?: 0L
    val cacheTimeout = TimeUnit.HOURS.toMillis(1)
    return if (System.currentTimeMillis() - lastUpdate >= cacheTimeout) {
        InitializeAction.LAUNCH_INITIAL_REFRESH
    } else {
        InitializeAction.SKIP_INITIAL_REFRESH
    }
}

Pemeriksaan Singkat

Dalam penyiapan RemoteMediator, apa satu-satunya sumber kebenaran yang digunakan antarmuka pengguna untuk memuat halaman?

Ringkasan: RemoteMediator dan Caching

Anda telah membuat pipeline pemuatan halaman offline-first:

  • Antarmuka pengguna memuat halaman dari PagingSource Room—satu-satunya sumber kebenaran
  • RemoteMediator.load() menangani REFRESH/PREPEND/APPEND dan menulis hasil ke Room dalam sebuah transaksi
  • Tabel kunci jarak jauh menyimpan kunci berikutnya/sebelumnya setelah aplikasi dimulai ulang
  • Pager menerima remoteMediator dan pagingSourceFactory
  • initialize() mengatur kesegaran cache

Sekarang Anda dapat memuat daftar tanpa akhir secara efisien, baik online maupun offline. Selamat telah menyelesaikan kursus Paging 3.

Gratis untuk memulai

Belajar Kotlin dengan tutor AI — gratis

Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.

Kursus
36
Pelajaran
152

Pertanyaan yang Sering Diajukan

Apakah pelajaran “RemoteMediator dan Caching” gratis?

Ya — teks lengkap “RemoteMediator dan Caching” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Android Academy, upgrade ke CoddyKit PRO. Kursus Android Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “RemoteMediator dan Caching”?

Gabungkan paging jaringan dan basis data. Kamu berlatih Android Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Android Academy?

Tidak diperlukan pengalaman sebelumnya. Android Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.

Berapa lama pelajaran “RemoteMediator dan Caching” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Android Academy ini?

Ya. Setiap pelajaran Android Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Mengapa Paging
  2. PagingSource dan Pager
  3. Paging dalam Daftar Compose
  4. RemoteMediator dan Caching
← Kembali ke Android Academy