Saat membangun aplikasi Android modern yang menampilkan banyak data dari server (seperti daftar produk, linimasa media sosial, atau daftar kontak), mengunduh seluruh data sekaligus bukanlah praktik yang baik. Hal tersebut akan menghabiskan kuota internet pengguna, membebani memori perangkat, dan membuat aplikasi terasa lambat. Di sinilah Pagination (pembagian data dalam beberapa halaman) berperan penting.
Paging 3 adalah library resmi dari Google (bagian dari Android Jetpack) yang dirancang khusus untuk menangani pagination dengan cara yang paling efisien.
Kenapa kita butuh Paging 3?
- Efisiensi Memori & Jaringan: Hanya memuat data yang sedang dilihat oleh pengguna dan akan mengambil data baru secara otomatis saat pengguna melakukan scroll ke bawah.
- Dukungan Asynchronous Bawaan: Sepenuhnya mendukung Kotlin Coroutines dan Flow, membuat kode jauh lebih bersih tanpa callback hell.
- Manajemen State Otomatis: Paging 3 secara otomatis menangani status loading, error, dan refresh. Kita tidak perlu lagi membuat logika rumit untuk menyembunyikan atau menampilkan progress bar.
- Caching: Mampu menyimpan data sementara di memori (atau database lokal seperti Room) untuk mencegah request jaringan berulang yang tidak perlu.
Langkah Implementasi & Penjelasan
Berikut adalah panduan langkah demi langkah beserta penjelasannya untuk mengimplementasikan Paging 3 mengambil data dari API.
1. Menambahkan Dependensi
Tambahkan library Paging 3 di dalam file build.gradle (level app).
dependencies {
def paging_version = "3.2.1"
// Paging 3 untuk RecyclerView (opsional jika menggunakan View System)
implementation "androidx.paging:paging-runtime:$paging_version"
// Paging 3 untuk Jetpack Compose (opsional jika menggunakan Compose)
implementation "androidx.paging:paging-compose:$paging_version"
}
Penjelasan: Dependensi ini memasukkan komponen utama Paging 3 ke dalam proyek Anda. Pilih
paging-composejika Anda sudah bermigrasi ke UI modern Android.
2. Membuat API Service (Retrofit)
Siapkan endpoint API yang mendukung sistem page atau offset.
interface ApiService {
@GET("users")
suspend fun getUsers(
@Query("page") page: Int,
@Query("per_page") perPage: Int
): Response<UserResponse>
}
Penjelasan: Pada contoh implementasi API ini, server mengharapkan parameter
pageuntuk menentukan halaman ke berapa yang ingin diambil, danper_pageuntuk menentukan jumlah data per halaman. Fungsi ini menggunakansuspendkarena kita akan memanggilnya dari dalam Coroutine.
3. Membuat PagingSource
PagingSource adalah komponen inti yang bertugas mengambil data dari sumber (dalam hal ini, jaringan/API).
class UserPagingSource(
private val apiService: ApiService
) : PagingSource<Int, User>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, User> {
return try {
// Tentukan halaman saat ini, default ke 1 jika null
val currentPage = params.key ?: 1
// Panggil API
val response = apiService.getUsers(
page = currentPage,
perPage = params.loadSize
)
val data = response.body()?.users ?: emptyList()
// Kembalikan hasil yang sukses
LoadResult.Page(
data = data,
prevKey = if (currentPage == 1) null else currentPage - 1,
nextKey = if (data.isEmpty()) null else currentPage + 1
)
} catch (e: Exception) {
// Tangkap dan kembalikan status error
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, User>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
load(): Fungsi ini dieksekusi secara asinkron.params.keyberisi nomor halaman yang sedang di-request.LoadResult.Page: Jika API sukses membalas, kita bungkus datanya di sini. Kita juga harus mendefinisikanprevKey(halaman sebelumnya) dannextKey(halaman berikutnya). JikanextKeydiset null, Paging 3 tahu bahwa tidak ada lagi data yang bisa di-load (end of pagination).LoadResult.Error: Digunakan jika terjadi gagal koneksi atau exception, membuat Paging 3 otomatis masuk ke error state.getRefreshKey(): Fungsi ini memberitahu Paging 3 dari halaman mana ia harus memulai kembali ketika data di-refresh atau divalidasi ulang (misalnya saat orientasi layar berubah).
4. Setup Repository dengan Pager
Di layer Repository, kita membuat objek Pager yang akan menghasilkan aliran data (Flow).
class UserRepository(private val apiService: ApiService) {
fun getUsers(): Flow<PagingData<User>> {
return Pager(
config = PagingConfig(
pageSize = 20,
enablePlaceholders = false
),
pagingSourceFactory = { UserPagingSource(apiService) }
).flow
}
}
Pager: Menghubungkan UI denganPagingSource.
PagingConfig: Mengatur perilaku pagination.pageSize = 20berarti Paging 3 akan mencoba mengambil 20 item per permintaan.enablePlaceholders = falsemematikan tampilan ruang kosong (placeholder) jika total data belum diketahui.
pagingSourceFactory: Tempat kita menginisialisasi kelasPagingSourceyang telah dibuat sebelumnya.
5. Mengonsumsi PagingData di ViewModel
ViewModel bertugas mengambil Flow dari Repository dan mempertahankannya agar selamat dari perubahan konfigurasi UI (seperti rotasi layar).
class UserViewModel(private val repository: UserRepository) : ViewModel() {
val usersFlow: Flow<PagingData<User>> = repository.getUsers()
.cachedIn(viewModelScope)
}
Penjelasan: Fungsi
cachedIn(viewModelScope)sangat krusial. Fungsi ini menyimpan data yang sudah diunduh (PagingData) di dalam cache yang terikat pada lifecycle ViewModel. Ini memastikan bahwa jika pengguna memutar layar, aplikasi tidak perlu me-request ulang data dari halaman 1.
Kesimpulan
Menggunakan Paging 3 mungkin terasa memiliki banyak boilerplate code (kode dasar) di awal, seperti keharusan membuat PagingSource, Pager, dan memetakan LoadResult. Namun, pengorbanan kecil di awal ini memberikan keuntungan luar biasa untuk jangka panjang.
Dengan Paging 3, Anda mendapatkan aplikasi yang jauh lebih stabil, hemat memori, dan mulus saat melakukan proses scroll tak terbatas (infinite scroll). Masalah kompleks seperti rotasi layar, penanganan status error di tengah list, hingga mencegah API terpanggil berulang kali, semuanya sudah ditangani secara elegan di balik layar oleh Paging 3 dan Kotlin Coroutines.
