Library Paging menyediakan kemampuan andal untuk memuat dan menampilkan data yang di-page dari set data yang lebih besar. Panduan ini menunjukkan cara menggunakan Library Paging untuk menyiapkan aliran data yang di-page dari sumber data jaringan dan menampilkannya di daftar lambat.
Menentukan sumber data
Langkah pertama adalah menentukan implementasi PagingSource untuk mengidentifikasi sumber data. Class PagingSource API mencakup metode load,
yang Anda ganti untuk menunjukkan cara mengambil data yang dibagi-bagi dari
sumber data yang sesuai.
Gunakan class PagingSource secara langsung untuk menggunakan coroutine Kotlin untuk pemuatan
asinkron.
Memilih jenis kunci dan nilai
PagingSource<Key, Value> memiliki dua jenis parameter: Key dan Value. Kunci
menentukan ID yang digunakan untuk memuat data, dan nilainya adalah jenis
data itu sendiri. Misalnya, jika Anda memuat halaman objek User dari jaringan
dengan meneruskan nomor halaman Int ke Retrofit, pilih Int sebagai jenis Key
dan User sebagai jenis Value.
Menentukan PagingSource
Contoh berikut menerapkan PagingSource yang memuat halaman item
menurut nomor halaman. Jenis Key adalah Int dan jenis Value adalah User.
class ExamplePagingSource(
val backend: ExampleBackendService,
val query: String
) : PagingSource<Int, User>() {
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, User> {
init {
// the data source is expected to be immutable
// invalidate PagingSource if data source
// has updated
backEnd.addDatabaseOnChangedListener {
invalidate()
}
}
try {
// Start refresh at page 1 if undefined.
val nextPageNumber = params.key ?: 1
val response = backend.searchUsers(query, nextPageNumber)
return LoadResult.Page(
data = response.users,
prevKey = null, // Only paging forward.
nextKey = nextPageNumber + 1
)
} catch (e: Exception) {
// Handle errors in this block and return LoadResult.Error for
// expected errors (such as a network failure).
}
}
override fun getRefreshKey(state: PagingState<Int, User>): Int? {
// Try to find the page key of the closest page to anchorPosition from
// either the prevKey or the nextKey; you need to handle nullability
// here.
// * prevKey == null -> anchorPage is the first page.
// * nextKey == null -> anchorPage is the last page.
// * both prevKey and nextKey are null -> anchorPage is the
// initial page, so return null.
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
Penerapan PagingSource standar akan meneruskan parameter yang diberikan dalam
konstruktornya ke metode load untuk memuat data yang sesuai untuk kueri. Dalam
contoh di atas, parameter tersebut adalah:
backend: instance layanan backend yang menyediakan data tersebutquery: kueri penelusuran yang akan dikirim ke layanan yang ditunjukkan olehbackend
Objek LoadParams
berisi informasi tentang operasi pemuatan yang akan dilakukan. Ini
termasuk kunci dan jumlah item yang akan dimuat.
Objek LoadResult
berisi hasil operasi pemuatan. LoadResult adalah class tertutup
yang mengambil salah satu dari tiga bentuk, bergantung pada apakah panggilan load berhasil:
- Jika pemuatan berhasil, tampilkan objek
LoadResult.Page. - Jika pemuatan gagal, tampilkan objek
LoadResult.Error. - Jika
PagingSourcetidak lagi valid dan harus diganti dengan instance baru (misalnya, karena perubahan data pokok), kembalikan objekLoadResult.Invalid.
Gambar berikut menggambarkan cara fungsi load dalam contoh ini
menerima kunci untuk setiap pemuatan dan memberikan kunci untuk pemuatan berikutnya.
load menggunakan dan memperbarui kunci.
Implementasi PagingSource juga harus menerapkan metode
getRefreshKey
yang memerlukan objek
PagingState sebagai
parameter. Metode ini menampilkan kunci untuk diteruskan ke metode load saat data
dimuat ulang atau dibatalkan validasinya setelah pemuatan awal. Library Paging memanggil
metode ini secara otomatis saat refresh data berikutnya.
Menangani error
Permintaan untuk memuat data dapat gagal karena sejumlah alasan, terutama saat memuat
melalui jaringan. Laporkan error yang terjadi selama pemuatan dengan menampilkan
objek LoadResult.Error dari metode load.
Misalnya, Anda dapat menangkap dan melaporkan kesalahan pemuatan di ExamplePagingSource
dari contoh sebelumnya dengan menambahkan hal berikut ke metode load:
catch (e: IOException) {
// IOException for network failures.
return LoadResult.Error(e)
} catch (e: HttpException) {
// HttpException for any non-2xx HTTP status codes.
return LoadResult.Error(e)
}
Untuk informasi selengkapnya tentang penanganan kesalahan Retrofit, lihat contoh dalam
referensi API PagingSource.
PagingSource mengumpulkan dan mengirimkan objek LoadResult.Error ke UI, sehingga
Anda dapat menindaklanjutinya. Untuk mengetahui informasi selengkapnya tentang menampilkan status pemuatan
di UI, lihat Mengelola dan menampilkan status pemuatan.
Menyiapkan aliran PagingData
Selanjutnya, Anda memerlukan aliran data yang dibagi-bagi dari implementasi PagingSource.
Menyiapkan aliran data di ViewModel Anda. Class Pager menyediakan
metode yang menampilkan aliran reaktif objek PagingData dari
PagingSource. Library Paging mengekspos aliran data sebagai Flow.
Saat membuat instance Pager untuk menyiapkan aliran reaktif, Anda harus
menyediakan instance dengan objek konfigurasi PagingConfig dan
fungsi yang memberi tahu Pager cara mendapatkan instance dari implementasi PagingSource
Anda, seperti yang ditunjukkan dalam contoh berikut.
class UserViewModel(
private val backend: ExampleBackendService,
private val query: String
) : ViewModel() {
val userPagingFlow: Flow<PagingData<User>> = Pager(
// Configure how data is loaded by passing additional properties to
// PagingConfig, such as pageSize and enabling or disabling placeholders.
config = PagingConfig(
pageSize = 20,
enablePlaceholders = true
),
pagingSourceFactory = {
ExamplePagingSource(backend, query)
}
)
.flow
.cachedIn(viewModelScope)
}
Operator cachedIn membuat aliran data dapat dibagikan dan menyimpan data yang dimuat ke cache
dengan CoroutineScope yang disediakan. Tanpa cachedIn, PagingData
tidak dapat diingat kembali. Contoh ini menggunakan viewModelScope yang disediakan oleh
artefak lifecycle-viewmodel-ktx siklus proses.
Objek Pager memanggil metode load dari objek PagingSource,
yang menyediakannya dengan objek LoadParams dan sebaliknya menerima
objek LoadResult.
Mengumpulkan dan menampilkan data di UI Anda
Untuk menghubungkan aliran yang di-paging ke UI, dapatkan aliran dari ViewModel Anda dan teruskan ke composable daftar Anda.
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
val userFlow = viewModel.userPagingFlow
UserList(flow = userFlow)
}
Gunakan collectAsLazyPagingItems untuk mengonversi alur PagingData menjadi
LazyPagingItems. Kemudian, gunakan items API dalam LazyColumn untuk menata
setiap item.
Pastikan untuk memberikan ID yang unik dan stabil untuk setiap item menggunakan itemKey.
Contoh berikut menggunakan it.id (mereferensikan properti User.id) karena tetap stabil untuk instance User di seluruh pembaruan data.
@Composable
fun UserList(flow: Flow<PagingData<User>>) {
val lazyPagingItems = flow.collectAsLazyPagingItems()
LazyColumn {
items(
lazyPagingItems.itemCount,
key = lazyPagingItems.itemKey { it.id }
) { index ->
val user = lazyPagingItems[index]
if (user != null) {
UserRow(user)
} else {
UserPlaceholder()
}
}
}
}
Library Paging menggunakan null untuk placeholder saat halaman dimuat, jadi jika Anda telah mengaktifkan placeholder, Anda harus menangani nilai null di blok konten.
Sekarang daftar menampilkan data yang di-page, dan library Paging memuat halaman tambahan saat pengguna men-scroll.
Referensi lainnya
Untuk mempelajari library Paging lebih lanjut, lihat referensi tambahan berikut:
Dokumentasi
Melihat konten
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Halaman dari jaringan dan database
- Bermigrasi ke Paging 3
- Ringkasan library paging