CategoriesAndroidProgramming

Membuat Pagination di Android dengan Paging 3

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?

  1. Efisiensi Memori & Jaringan: Hanya memuat data yang sedang dilihat oleh pengguna dan akan mengambil data baru secara otomatis saat pengguna melakukan scroll ke bawah.
  2. Dukungan Asynchronous Bawaan: Sepenuhnya mendukung Kotlin Coroutines dan Flow, membuat kode jauh lebih bersih tanpa callback hell.
  3. 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.
  4. 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-compose jika 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 page untuk menentukan halaman ke berapa yang ingin diambil, dan per_page untuk menentukan jumlah data per halaman. Fungsi ini menggunakan suspend karena 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.key berisi nomor halaman yang sedang di-request.
  • LoadResult.Page: Jika API sukses membalas, kita bungkus datanya di sini. Kita juga harus mendefinisikan prevKey (halaman sebelumnya) dan nextKey (halaman berikutnya). Jika nextKey diset 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 dengan PagingSource.
  • PagingConfig: Mengatur perilaku pagination. pageSize = 20 berarti Paging 3 akan mencoba mengambil 20 item per permintaan. enablePlaceholders = false mematikan tampilan ruang kosong (placeholder) jika total data belum diketahui.
  • pagingSourceFactory: Tempat kita menginisialisasi kelas PagingSource yang 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.

Published by Ahmad Saifur Ridlo

Android Developer at Algostudio.net