RemoteMediator und Caching
Paging für Netzwerk und Datenbank kombinieren
RemoteMediator und Caching ist eine kostenlose Android Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Android Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Android Academy-Kurs umfasst insgesamt 4 Lektionen.
Offline-First-Paging
Eine reine Netzwerk-PagingSource funktioniert, aber wenn die Verbindung abbricht, ist die Liste leer. Für eine robuste App sollte die Datenbank die einzige Quelle der Wahrheit sein und im Hintergrund vom Netzwerk befüllt werden.
Genau das ermöglicht RemoteMediator: Die Seiten werden aus der lokalen Datenbank geladen, und weitere Daten werden aus dem Netzwerk abgerufen, sobald die Datenbank keine mehr enthält.
Die Architektur
Mit RemoteMediator fließen die Daten in eine Richtung:
- Die UI lädt Seiten aus einer Room-
PagingSource - Wenn Room das Ende erreicht, ruft
RemoteMediatordie nächste Netzwerkseite ab - Das Netzwerkergebnis wird in Room geschrieben
- Room gibt die neuen Zeilen aus und die UI wird aktualisiert
Die Benutzer sehen zwischengespeicherte Daten sofort, auch offline.
// Network ---> RemoteMediator ---> Room (source of truth) ---> UI
// UI never reads the network directlyRoom-PagingSource ohne Zusatzaufwand
Room kann automatisch eine PagingSource generieren. Deklarieren Sie einfach eine Abfrage, die PagingSource<Int, Entity> zurückgibt, und Room implementiert sie.
Aus dieser Quelle lädt die UI tatsächlich ihre Seiten.
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>
}RemoteMediator ableiten
RemoteMediator<Key, Value> verfügt über eine erforderliche Methode, load(), sowie eine optionale Methode, initialize().
load() erhält einen LoadType (REFRESH, PREPEND oder APPEND) und den aktuellen PagingState und gibt anschließend ein MediatorResult zurück.
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
}LoadType verarbeiten
In load() entscheiden Sie anhand des LoadType, welche Seite abgerufen werden soll:
- REFRESH – die erste Seite laden (oder den Bereich um den Anker)
- PREPEND – normalerweise nichts zu tun;
endOfPaginationReached = truezurückgeben - APPEND – die Seite nach der letzten vorhandenen Seite laden
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
}
}Tabelle für Remote Keys
Anders als eine PagingSource kann ein RemoteMediator die Schlüssel für die nächste bzw. vorherige Seite nicht direkt zurückgeben – sie müssen dauerhaft gespeichert werden. Speichern Sie sie zusammen mit Ihren Daten in einer kleinen Tabelle für remote keys.
So können Sie APPEND/PREPEND nach einem Neustart der App fortsetzen.
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?
)Netzwerkergebnisse in Room schreiben
Schreiben Sie die Elemente und ihre Schlüssel nach dem Abrufen einer Seite innerhalb einer Transaktion in Room. Leeren Sie bei REFRESH zuerst die alten Tabellen, damit der Cache konsistent bleibt.
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() })
}MediatiorResult zurückgeben
Beenden Sie load(), indem Sie ein MediatorResult zurückgeben:
Success(endOfPaginationReached = true), wenn keine weiteren Seiten vorhanden sindSuccess(endOfPaginationReached = false), wenn weitere Seiten vorhanden sein könntenError(e)bei einem Fehler – dieser wird alsLoadState.Errorweitergegeben
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)
}Pager mit remoteMediator verbinden
Erstellen Sie den Pager sowohl mit dem remoteMediator als auch mit der Room-pagingSourceFactory. Der Mediator befüllt Room; die UI lädt Seiten aus Room.
Da hierfür eine experimentelle API verwendet wird, müssen Sie sie mit @OptIn(ExperimentalPagingApi::class) aktivieren.
@OptIn(ExperimentalPagingApi::class)
fun articles(): Flow<PagingData<ArticleEntity>> = Pager(
config = PagingConfig(pageSize = 20),
remoteMediator = ArticleRemoteMediator(api, db),
pagingSourceFactory = { db.articleDao().pagingSource() }
).flowSchlüssel für APPEND nachschlagen
Für einen APPEND-Ladevorgang benötigen Sie den nextKey des zuletzt geladenen Elements. Verwenden Sie den Anker im PagingState, um den passenden Remote Key aus Ihrer Schlüsseltabelle zu ermitteln.
Hilfsfunktionen sorgen dafür, dass load() für REFRESH, APPEND und PREPEND übersichtlich bleibt.
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 und Aktualität
Da die Daten in Room gespeichert werden, lädt die App sofort und funktioniert offline. Um veraltete Inhalte zu vermeiden, überschreiben Sie initialize(), um anhand des Alters des Caches zu entscheiden, ob beim Start aktualisiert werden soll.
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
}
}Kurze Überprüfung
Welche Komponente ist in einer RemoteMediator-Konfiguration die einzige Quelle der Wahrheit, aus der die UI ihre Seiten lädt?
Zusammenfassung: RemoteMediator und Caching
Sie haben eine Offline-First-Paging-Pipeline erstellt:
- Die UI lädt Seiten aus einer Room-
PagingSource– der einzigen Quelle der Wahrheit RemoteMediator.load()verarbeitetREFRESH/PREPEND/APPENDund schreibt die Ergebnisse innerhalb einer Transaktion in Room- Eine Tabelle für remote keys speichert die Schlüssel für die nächste bzw. vorherige Seite über Neustarts hinweg
- Der
Pagererhält sowohl einenremoteMediatorals auch einepagingSourceFactory initialize()steuert die Aktualität des Caches
Sie können nun endlose Listen online oder offline effizient laden. Herzlichen Glückwunsch zum Abschluss des Kurses zu Paging 3.
Häufig gestellte Fragen
Ist die Lektion „RemoteMediator und Caching“ kostenlos?
Ja — der vollständige Text von „RemoteMediator und Caching“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Android Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Android Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „RemoteMediator und Caching“?
Paging für Netzwerk und Datenbank kombinieren Du übst Android Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Android Academy zu starten?
Keine Vorkenntnisse erforderlich. Android Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.
Wie lange dauert die Lektion „RemoteMediator und Caching“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Android Academy-Lektion Code schreiben und ausführen?
Ja. Jede Android Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Warum Paging?
- PagingSource und Pager
- Paging in Compose-Listen
- RemoteMediator und Caching