Android Academy · Pelajaran

RemoteMediator dan Pencachean

Gabungkan pemenggalan rangkaian dan pangkalan data.

Pelajaran 4 daripada 413 langkah

RemoteMediator dan Pencachean ialah pelajaran Android Academy percuma di CoddyKit. Ini ialah pelajaran 4 daripada 4. Sebanyak 3 pelajaran dalam laluan pembelajaran ini boleh dibaca sepenuhnya secara percuma — selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan praktikal dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Android Academy, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Android Academy merangkumi sejumlah 4 pelajaran.

Penomboran Luar Talian Terlebih Dahulu

PagingSource rangkaian biasa boleh berfungsi, tetapi apabila sambungan terputus, senarai menjadi kosong. Untuk aplikasi yang teguh, anda mahukan pangkalan data sebagai sumber kebenaran tunggal dan rangkaian mengisinya di latar belakang.

Itulah yang didayakan oleh RemoteMediator: halaman diambil daripada pangkalan data setempat, dan data tambahan diambil daripada rangkaian apabila pangkalan data kehabisan data.

Seni Bina

Dengan RemoteMediator, data mengalir dalam satu arah:

  • UI mendapatkan halaman daripada PagingSource Room
  • Apabila Room menghampiri penghujungnya, RemoteMediator mengambil halaman rangkaian seterusnya
  • Hasil rangkaian ditulis ke dalam Room
  • Room memancarkan baris baharu, lalu UI dikemas kini

Pengguna sentiasa melihat data cache dengan serta-merta, walaupun di luar talian.

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

PagingSource Room Secara Automatik

Room boleh menjana PagingSource secara automatik. Anda hanya perlu mengisytiharkan pertanyaan yang mengembalikan PagingSource<Int, Entity>, dan Room akan melaksanakannya.

Inilah sumber yang sebenarnya digunakan oleh UI untuk mendapatkan 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>
}

Mewarisi RemoteMediator

RemoteMediator<Key, Value> mempunyai satu kaedah yang diperlukan, iaitu load(), serta initialize() yang pilihan.

load() menerima LoadType (REFRESH, PREPEND atau APPEND) dan PagingState semasa, kemudian 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
}

Mengendalikan LoadType

Dalam load(), anda menentukan halaman yang hendak diambil berdasarkan LoadType:

  • REFRESH - memuatkan halaman pertama (atau halaman di sekitar penambat)
  • PREPEND - biasanya tiada apa-apa yang perlu dilakukan; kembalikan endOfPaginationReached = true
  • APPEND - memuatkan halaman selepas halaman terakhir yang kita ada
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
    }
}

Jadual Kunci Jauh

Tidak seperti PagingSource, RemoteMediator tidak boleh mengembalikan kunci seterusnya/sebelumnya secara terus - kunci tersebut mesti disimpan. Simpannya dalam jadual kunci jauh yang kecil bersama-sama data anda.

Ini membolehkan anda menyambung semula APPEND/PREPEND selepas aplikasi dimulakan semula.

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 Rangkaian ke Room

Selepas mengambil halaman, tulis item dan kuncinya ke dalam Room dalam satu transaksi. Semasa REFRESH, kosongkan jadual lama terlebih dahulu supaya cache kekal 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

Lengkapkan load() dengan mengembalikan MediatorResult:

  • Success(endOfPaginationReached = true) apabila tiada lagi halaman
  • Success(endOfPaginationReached = false) apabila mungkin masih ada halaman
  • Error(e) apabila berlaku kegagalan - dipaparkan 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)
}

Menyambungkan Pager dengan remoteMediator

Bina Pager dengan kedua-dua remoteMediator dan pagingSourceFactory Room. Mediator mengisi Room; UI mendapatkan halaman daripada Room.

Oleh sebab ini menggunakan API percubaan, aktifkan penyertaan 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 bagi item terakhir yang dimuatkan. Gunakan penambat PagingState untuk mencari kunci jauh yang betul daripada jadual kunci anda.

Fungsi pembantu menjadikan load() lebih mudah dibaca merentasi 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)

Pencachean dan Kesegaran

Oleh sebab data berada dalam Room, aplikasi dimuatkan serta-merta dan berfungsi di luar talian. Untuk mengelakkan kandungan lapuk, gantikan initialize() bagi menentukan sama ada perlu melakukan muat semula semasa pelancaran 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
    }
}

Semakan Pantas

Dalam persediaan RemoteMediator, apakah sumber kebenaran tunggal yang digunakan oleh UI untuk mendapatkan halaman?

Imbas Kembali: RemoteMediator dan Pencachean

Anda telah membina saluran penomboran luar talian terlebih dahulu:

  • UI mendapatkan halaman daripada PagingSource Room - sumber kebenaran tunggal
  • RemoteMediator.load() mengendalikan REFRESH/PREPEND/APPEND dan menulis hasil ke Room dalam satu transaksi
  • Jadual kunci jauh menyimpan kunci seterusnya/sebelumnya selepas aplikasi dimulakan semula
  • Pager menerima kedua-dua remoteMediator dan pagingSourceFactory
  • initialize() mengawal kesegaran cache

Percuma untuk bermula

Pelajari Kotlin dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
36
Pelajaran
152

Soalan Lazim

Adakah pelajaran “RemoteMediator dan Pencachean” percuma?

Ya — sebanyak 3 pelajaran dalam laluan pembelajaran Android Academy, termasuk “RemoteMediator dan Pencachean”, boleh dibaca sepenuhnya secara percuma di web ini. Selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan interaktif dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Kursus Android Academy merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “RemoteMediator dan Pencachean”?

Gabungkan pemenggalan rangkaian dan pangkalan data. Anda berlatih Android Academy menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan Android Academy?

Tiada pengalaman terdahulu diperlukan. Pembelajaran Android Academy di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 4 daripada 4.

Berapa lamakah pelajaran “RemoteMediator dan Pencachean” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran Android Academy ini?

Ya. Setiap pelajaran Android Academy menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Sebab Pemenggalan
  2. PagingSource dan Pager
  3. Pemenggalan dalam Senarai Compose
  4. RemoteMediator dan Pencachean
← Kembali ke Android Academy