PagingSource og Pager
Definér, hvordan siderne indlæses.
PagingSource og Pager er en gratis Android Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Android Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Android Academy-kurset indeholder 4 lektioner i alt.
Definér, hvordan sider indlæses
Hvis du vil indlæse sider fra en netværks-API, skriver du en PagingSource. Den besvarer to spørgsmål for Paging 3:
- Hvordan indlæser jeg siden for en given nøgle?
- Hvor skal jeg fortsætte fra, hvis brugeren opdaterer?
Derefter pakker du den ind i en Pager, som omdanner den til en Flow<PagingData>.
Typeparametre for PagingSource
PagingSource<Key, Value> tager to typeparametre:
- Key - identificerer en side. For en API med sidetal er den
Int; for en cursor-API er den etString-token. - Value - elementtypen, f.eks.
Article.
import androidx.paging.PagingSource
import androidx.paging.PagingState
class ArticlePagingSource(
private val api: ArticleApi
) : PagingSource<Int, Article>() {
// implement load() and getRefreshKey()
}Implementering af load()
load() er en suspend-funktion. Den modtager params.key (den side, der skal hentes) og returnerer et LoadResult.
Ved succes returnerer du LoadResult.Page med elementerne samt de forrige og næste nøgler. null for en nøgle betyder, at der ikke findes en side i den retning.
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, Article> {
val page = params.key ?: 1 // first load has a null key
return try {
val response = api.getArticles(page = page, size = params.loadSize)
LoadResult.Page(
data = response.items,
prevKey = if (page == 1) null else page - 1,
nextKey = if (response.items.isEmpty()) null else page + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}Hvorfor prevKey og nextKey er vigtige
Paging bruger nextKey til at indlæse fremad, når brugeren ruller ned, og prevKey til at indlæse bagud (nyttigt, når man starter midt i en liste).
Hvis du returnerer null for nextKey, fortæller du Paging, at der ikke er flere sider, så den stopper med at sende anmodninger. Hvis du glemmer dette, kan det føre til uendeligt mange tomme anmodninger.
// Stop forward paging when the server returns an empty page
nextKey = if (response.items.isEmpty()) null else page + 1
// Stop backward paging at the first page
prevKey = if (page == 1) null else page - 1Implementering af getRefreshKey()
Når listen opdateres (ved træk for at opdatere eller ugyldiggørelse), skal Paging vide, hvilken side der skal indlæses igen, så brugeren omtrent bliver på samme sted.
getRefreshKey() bruger den aktuelle anchorPosition - elementet nærmest visningsområdet - til at vælge en fornuftig nøgle.
override fun getRefreshKey(state: PagingState<Int, Article>): Int? {
return state.anchorPosition?.let { anchor ->
val closestPage = state.closestPageToPosition(anchor)
closestPage?.prevKey?.plus(1)
?: closestPage?.nextKey?.minus(1)
}
}Håndter fejl på en god måde
Pak netværkskaldet ind i try/catch, og returner LoadResult.Error(e) ved fejl. Paging gør dette til en LoadState.Error i brugergrænsefladen, så du kan vise en knap til at prøve igen.
Lad aldrig en undtagelse slippe ud af load() - fang den, og konvertér den til LoadResult.Error.
return try {
val response = api.getArticles(page = page, size = params.loadSize)
LoadResult.Page(
data = response.items,
prevKey = if (page == 1) null else page - 1,
nextKey = if (response.items.isEmpty()) null else page + 1
)
} catch (e: IOException) { // no network
LoadResult.Error(e)
} catch (e: HttpException) { // non-2xx response
LoadResult.Error(e)
}Opret en Pager
En Pager forbinder din PagingConfig med en fabrik, der opretter en ny PagingSource. Dens egenskab .flow er en Flow<PagingData>, som brugergrænsefladen indsamler.
Fabrikkens lambda skal oprette en ny kilde hver gang, fordi Paging ugyldiggør og opretter kilden igen ved opdatering.
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
class ArticleRepository(private val api: ArticleApi) {
fun articleStream(): Flow<PagingData<Article>> = Pager(
config = PagingConfig(pageSize = 20, prefetchDistance = 5),
pagingSourceFactory = { ArticlePagingSource(api) }
).flow
}cachedIn til ViewModels
Indsamling af PagingData er en engangsoperation; hvis du indsamler igen, starter indlæsningen forfra. Hvis data skal overleve konfigurationsændringer, og flere indsamlere skal kunne dele dem, skal du cache datastrømmen i viewModelScope med cachedIn.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.cachedIn
class ArticleViewModel(
repo: ArticleRepository
) : ViewModel() {
val articles = repo.articleStream()
.cachedIn(viewModelScope)
}loadSize kontra pageSize
Bemærk, at load() læser params.loadSize og ikke direkte din konfigurerede pageSize.
Ved den allerførste indlæsning kan Paging anmode om en større indledende blok (styret af initialLoadSize i PagingConfig, som som standard er tre gange sidestørrelsen). Send altid params.loadSize til din API, så anmodningen svarer til det, Paging forventer at modtage.
// Correct: respect the size Paging asks for
val response = api.getArticles(page = page, size = params.loadSize)
// PagingConfig can tune the first load:
PagingConfig(pageSize = 20, initialLoadSize = 40)Cursor-baserede API'er
Ikke alle API'er bruger sidetal. Nogle returnerer en cursor eller et token, der peger på den næste side. Mønstret er identisk - du skal blot ændre Key-typen til String og bruge tokenet fra svaret.
class CursorArticleSource(
private val api: ArticleApi
) : PagingSource<String, Article>() {
override suspend fun load(
params: LoadParams<String>
): LoadResult<String, Article> {
val cursor = params.key // null on first load
return try {
val res = api.getArticles(cursor = cursor, size = params.loadSize)
LoadResult.Page(
data = res.items,
prevKey = null, // forward-only cursor
nextKey = res.nextCursor // null when exhausted
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<String, Article>) = null
}Sæt det hele sammen
Du har nu hele datalaget: en PagingSource, der indlæser én side, en Pager, der sender sider som en datastrøm, og en ViewModel, der cacher datastrømmen.
Brugergrænsefladen indsamler ganske enkelt viewModel.articles - i den næste lektion viser vi dem med en Compose-LazyColumn.
// Data layer summary
// 1. ArticlePagingSource : PagingSource<Int, Article>
// 2. Pager(config, factory).flow -> Flow<PagingData<Article>>
// 3. ViewModel: repo.articleStream().cachedIn(viewModelScope)
// UI just collects viewModel.articlesHurtigt tjek
Hvad signalerer det i en PagingSource med sidetal, når load() returnerer nextKey = null?
Opsummering: PagingSource og Pager
Du har opbygget datalaget til sideindlæsning:
PagingSource<Key, Value>implementererload()oggetRefreshKey()load()returnererLoadResult.PagemedprevKey/nextKeyellerLoadResult.Error- En
null-nøgle stopper sideindlæsningen i den pågældende retning Pager(config, factory).flowproducerer enFlow<PagingData>cachedIn(viewModelScope)bevarer data på tværs af konfigurationsændringer
Næste trin: vis denne datastrøm i en Compose-liste.
Lær Kotlin med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 36
- Lektioner
- 152
Ofte stillede spørgsmål
Er lektionen “PagingSource og Pager” gratis?
Ja — alle 3 lektioner i læringssporet Android Academy, inklusive “PagingSource og Pager”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Android Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “PagingSource og Pager”?
Definér, hvordan siderne indlæses. Du øver dig i Android Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Android Academy?
Der kræves ingen tidligere erfaring. Android Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “PagingSource og Pager”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Android Academy-lektion?
Ja. Alle Android Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Hvorfor Paging
- PagingSource og Pager
- Paging i Compose-lister
- RemoteMediator og caching