Ringkasan library Paging 2 Bagian dari Android Jetpack.
Library Paging membantu Anda memuat dan menampilkan data sedikit demi sedikit. Memuat bagian data berdasarkan permintaan dapat mengurangi penggunaan bandwidth jaringan dan resource sistem.
Artikel ini menyajikan beberapa contoh konseptual tentang Library Paging, beserta ringkasan cara kerjanya. Untuk melihat contoh lengkap fungsi library ini, cobalah codelab dan sampel dari bagian referensi tambahan.
Penyiapan
Untuk mengimpor komponen Paging ke dalam aplikasi Android, tambahkan
dependensi berikut ke file build.gradle
aplikasi Anda:
Groovy
dependencies { def paging_version = "2.1.2" implementation "androidx.paging:paging-runtime:$paging_version" // For Kotlin use paging-runtime-ktx // alternatively - without Android dependencies for testing testImplementation "androidx.paging:paging-common:$paging_version" // For Kotlin use paging-common-ktx // optional - RxJava support implementation "androidx.paging:paging-rxjava2:$paging_version" // For Kotlin use paging-rxjava2-ktx }
Kotlin
dependencies { val paging_version = "2.1.2" implementation("androidx.paging:paging-runtime:$paging_version") // For Kotlin use paging-runtime-ktx // alternatively - without Android dependencies for testing testImplementation("androidx.paging:paging-common:$paging_version") // For Kotlin use paging-common-ktx // optional - RxJava support implementation("androidx.paging:paging-rxjava2:$paging_version") // For Kotlin use paging-rxjava2-ktx }
Arsitektur library
Bagian ini menjelaskan dan menampilkan beberapa komponen utama library paging.
PagedList
Komponen utama Library Paging adalah
class PagedList
, yang memuat
bagian data aplikasi Anda, atau page. Seiring meningkatnya jumlah data yang diperlukan,
data di-page ke dalam objek PagedList
yang ada. Jika terdapat perubahan pada data yang dimuat,
instance PagedList
baru akan dikirim ke penampung data yang dapat diamati dari
objek berbasis LiveData
atau RxJava2. Saat
objek PagedList
dihasilkan,
UI aplikasi Anda akan menyajikan kontennya, semuanya dengan tetap mempertahankan
siklus proses pengontrol UI Anda.
Cuplikan kode berikut menunjukkan cara mengonfigurasi model tampilan aplikasi untuk
memuat dan menyajikan data menggunakan penampung LiveData
dari objek PagedList
:
Kotlin
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: LiveData<PagedList<Concert>> = concertDao.concertsByDate().toLiveData(pageSize = 50) }
Java
public class ConcertViewModel extends ViewModel { private ConcertDao concertDao; public final LiveData<PagedList<Concert>> concertList; // Creates a PagedList object with 50 items per page. public ConcertViewModel(ConcertDao concertDao) { this.concertDao = concertDao; concertList = new LivePagedListBuilder<>( concertDao.concertsByDate(), 50).build(); } }
Data
Setiap instance PagedList
memuat
snapshot terbaru data aplikasi Anda dari objek
DataSource
terkaitnya. Data mengalir
dari backend atau database aplikasi Anda ke objek PagedList
.
Contoh berikut menggunakan library persistensi Room untuk mengatur data aplikasi Anda, tetapi jika ingin menyimpan data menggunakan cara lain, Anda juga dapat menyediakan sumber data sendiri.
Kotlin
@Dao interface ConcertDao { // The Int type parameter tells Room to use a PositionalDataSource object. @Query("SELECT * FROM concerts ORDER BY date DESC") fun concertsByDate(): DataSource.Factory<Int, Concert> }
Java
@Dao public interface ConcertDao { // The Integer type parameter tells Room to use a // PositionalDataSource object. @Query("SELECT * FROM concerts ORDER BY date DESC") DataSource.Factory<Integer, Concert> concertsByDate(); }
Untuk mempelajari cara memuat data ke objek PagedList
lebih lanjut, lihat
panduan cara Memuat data yang di-page.
UI
Class PagedList
berfungsi dengan
PagedListAdapter
untuk memuat item ke dalam
RecyclerView
. Class
ini bekerja sama untuk mengambil dan menampilkan konten saat dimuat, dengan mengambil data
konten di luar tampilan dan menganimasikan perubahan konten.
Untuk mempelajari lebih lanjut, lihat panduan cara Menampilkan daftar yang di-page.
Mendukung arsitektur data yang berbeda
Library Paging mendukung arsitektur data yang:
- Ditayangkan hanya dari server backend.
- Disimpan hanya di database dalam perangkat.
- Dikombinasikan dengan sumber lain, menggunakan database dalam perangkat sebagai cache.
Gambar 1 menunjukkan data yang mengalir di setiap skenario arsitektur ini. Dalam kasus solusi khusus jaringan atau khusus database, data mengalir langsung ke model UI aplikasi Anda. Jika Anda menggunakan pendekatan gabungan, data mengalir dari server backend, masuk ke database di perangkat, lalu ke model UI aplikasi Anda. Terkadang, endpoint setiap alur data kehabisan data yang harus dimuat, dan pada saat itu lebih banyak data akan diminta dari komponen yang menyediakan data. Misalnya, jika database pada perangkat kehabisan data, lebih banyak data akan diminta dari server.
Bagian selanjutnya dari artikel ini menyajikan rekomendasi untuk mengonfigurasi setiap kasus penggunaan alur data.
Khusus jaringan
Untuk menampilkan data dari server backend, gunakan Retrofit API
versi sinkron untuk memuat
informasi ke dalam objek DataSource
kustom Anda.
Khusus database
Siapkan RecyclerView
untuk mengamati penyimpanan lokal; sebaiknya gunakan library persistensi
Room. Dengan begitu, setiap kali data
dimasukkan atau diubah di database aplikasi, perubahan tersebut otomatis
tecermin dalam RecyclerView
yang menampilkan data ini.
Jaringan dan database
Setelah mulai mengamati database, Anda dapat memproses kapan
database kehabisan data menggunakan
PagedList.BoundaryCallback
.
Selanjutnya, Anda dapat mengambil lebih banyak item dari jaringan dan memasukkannya ke dalam
database. Jika UI Anda mengamati database, hanya itulah yang perlu Anda lakukan.
Menangani error jaringan
Saat menggunakan jaringan untuk mengambil atau mem-page data yang Anda tampilkan melalui Library Paging, jangan perlakukan jaringan sebagai "tersedia" atau "tidak tersedia" sepanjang waktu, mengingat banyak koneksi yang terputus-putus atau tidak stabil:
- Server tertentu dapat gagal merespons permintaan jaringan.
- Perangkat mungkin terhubung ke jaringan yang lambat atau lemah.
Sebagai gantinya, aplikasi Anda harus memeriksa kemungkinan terjadi kegagalan di setiap permintaan dan melakukan pemulihan selancar mungkin jika jaringan tidak tersedia. Misalnya, Anda dapat menyediakan tombol "coba lagi" yang dapat diklik pengguna jika pemuatan ulang data tidak menyelesaikan masalah. Jika error terjadi selama tahap paging data, sebaiknya ulangi permintaan paging secara otomatis.
Mengupdate aplikasi yang ada
Jika aplikasi Anda sudah menggunakan data dari database atau sumber backend, Anda dapat melakukan upgrade langsung ke fungsionalitas yang disediakan oleh Library Paging. Bagian ini menjelaskan cara mengupgrade aplikasi yang memiliki desain umum yang sudah ada.
Solusi paging kustom
Jika Anda menggunakan fungsi kustom untuk memuat sebagian kecil data dari
sumber data aplikasi, Anda dapat mengganti logika ini dengan sebagian data dari
class PagedList
. Instance
PagedList
menawarkan koneksi bawaan ke sumber data umum. Instance ini
juga menyediakan adaptor untuk
objek RecyclerView
yang
dapat Anda sertakan di UI aplikasi Anda.
Data yang dimuat menggunakan daftar, bukan halaman
Jika Anda menggunakan daftar dalam memori sebagai struktur data pendukung untuk adaptor
UI, sebaiknya amati pembaruan data menggunakan
class PagedList
jika jumlah
item dalam daftar sangat banyak. Instance PagedList
dapat menggunakan
LiveData<PagedList>
atau
Observable<List>
untuk meneruskan pembaruan data ke UI aplikasi Anda, sehingga meminimalkan waktu pemuatan
dan penggunaan memori. Lebih baik lagi, mengganti objek List
dengan objek PagedList
di aplikasi Anda tidak memerlukan perubahan apa pun pada
struktur UI aplikasi atau logika pembaruan data.
Mengaitkan kursor data dengan tampilan daftar menggunakan CursorAdapter
Aplikasi Anda mungkin menggunakan CursorAdapter
untuk mengaitkan data dari Cursor
dengan
ListView
. Dalam hal ini, biasanya Anda perlu
melakukan migrasi dari ListView
ke
RecyclerView
sebagai
penampung UI daftar aplikasi Anda, lalu mengganti komponen Cursor
dengan Room atau
PositionalDataSource
, bergantung pada apakah instance Cursor
mengakses
database SQLite atau tidak.
Dalam beberapa situasi, misalnya saat menangani instance
Spinner
, Anda hanya menyediakan adaptor
itu sendiri. Selanjutnya, library mengambil data yang dimuat ke adaptor tersebut dan
menampilkan data itu kepada Anda. Dalam situasi ini, ubah jenis data
adaptor Anda ke
LiveData<PagedList>
, lalu gabungkan
daftar ini dalam objek ArrayAdapter
sebelum meminta class library meng-inflate item ini di UI.
Memuat konten secara asinkron menggunakan AsyncListUtil
Jika Anda menggunakan
objek AsyncListUtil
untuk
memuat dan menampilkan kelompok informasi secara asinkron, Library Paging
memungkinkan Anda memuat data dengan lebih mudah:
- Data Anda tidak harus bergantung pada posisi. Library Paging memungkinkan Anda memuat data langsung dari backend menggunakan kunci yang disediakan oleh jaringan.
- Data Anda dapat berukuran sangat besar. Dengan Library Paging, Anda dapat memuat data ke dalam halaman hingga tidak ada lagi data yang tersisa.
- Anda dapat mengamati data dengan lebih mudah. Library Paging dapat menyajikan data yang ditampung oleh ViewModel aplikasi Anda dalam struktur data yang dapat diamati.
Contoh database
Cuplikan kode berikut menunjukkan beberapa kemungkinan untuk membuat semua bagian bekerja sama.
Mengamati data yang di-page menggunakan LiveData
Cuplikan kode berikut menunjukkan kerja sama antara semua bagian. Saat acara
konser ditambahkan, dihapus, atau diubah di database, konten di
RecyclerView
diperbarui
secara otomatis dan efisien:
Kotlin
@Dao interface ConcertDao { // The Int type parameter tells Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM concerts ORDER BY date DESC") fun concertsByDate(): DataSource.Factory<Int, Concert> } class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: LiveData<PagedList<Concert>> = concertDao.concertsByDate().toLiveData(pageSize = 50) } class ConcertActivity : AppCompatActivity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact val viewModel: ConcertViewModel by viewModels() val recyclerView = findViewById(R.id.concert_list) val adapter = ConcertAdapter() viewModel.concertList.observe(this, PagedList(adapter::submitList)) recyclerView.setAdapter(adapter) } } class ConcertAdapter() : PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) { fun onBindViewHolder(holder: ConcertViewHolder, position: Int) { val concert: Concert? = getItem(position) // Note that "concert" is a placeholder if it's null. holder.bindTo(concert) } companion object { private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() { // Concert details may have changed if reloaded from the database, // but ID is fixed. override fun areItemsTheSame(oldConcert: Concert, newConcert: Concert) = oldConcert.id == newConcert.id override fun areContentsTheSame(oldConcert: Concert, newConcert: Concert) = oldConcert == newConcert } } }
Java
@Dao public interface ConcertDao { // The Integer type parameter tells Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM concerts ORDER BY date DESC") DataSource.Factory<Integer, Concert> concertsByDate(); } public class ConcertViewModel extends ViewModel { private ConcertDao concertDao; public final LiveData<PagedList<Concert>> concertList; public ConcertViewModel(ConcertDao concertDao) { this.concertDao = concertDao; concertList = new LivePagedListBuilder<>( concertDao.concertsByDate(), /* page size */ 50).build(); } } public class ConcertActivity extends AppCompatActivity { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ConcertViewModel viewModel = new ViewModelProvider(this).get(ConcertViewModel.class); RecyclerView recyclerView = findViewById(R.id.concert_list); ConcertAdapter adapter = new ConcertAdapter(); viewModel.concertList.observe(this, adapter::submitList); recyclerView.setAdapter(adapter); } } public class ConcertAdapter extends PagedListAdapter<Concert, ConcertViewHolder> { protected ConcertAdapter() { super(DIFF_CALLBACK); } @Override public void onBindViewHolder(@NonNull ConcertViewHolder holder, int position) { Concert concert = getItem(position); if (concert != null) { holder.bindTo(concert); } else { // Null defines a placeholder item - PagedListAdapter automatically // invalidates this row when the actual object is loaded from the // database. holder.clear(); } } private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK = new DiffUtil.ItemCallback<Concert>() { // Concert details may have changed if reloaded from the database, // but ID is fixed. @Override public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) { return oldConcert.getId() == newConcert.getId(); } @Override public boolean areContentsTheSame(Concert oldConcert, Concert newConcert) { return oldConcert.equals(newConcert); } }; }
Mengamati data yang di-page menggunakan RxJava2
Jika lebih suka menggunakan
RxJava2 daripada
LiveData
, Anda dapat
membuat objek Observable
atau Flowable
:
Kotlin
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: Observable<PagedList<Concert>> = concertDao.concertsByDate().toObservable(pageSize = 50) }
Java
public class ConcertViewModel extends ViewModel { private ConcertDao concertDao; public final Observable<PagedList<Concert>> concertList; public ConcertViewModel(ConcertDao concertDao) { this.concertDao = concertDao; concertList = new RxPagedListBuilder<>( concertDao.concertsByDate(), /* page size */ 50) .buildObservable(); } }
Selanjutnya, Anda dapat mulai dan berhenti mengamati data menggunakan kode dalam cuplikan berikut:
Kotlin
class ConcertActivity : AppCompatActivity() { private val adapter = ConcertAdapter() // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact private val viewModel: ConcertViewModel by viewModels() private val disposable = CompositeDisposable() public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val recyclerView = findViewById(R.id.concert_list) recyclerView.setAdapter(adapter) } override fun onStart() { super.onStart() disposable.add(viewModel.concertList .subscribe(adapter::submitList))) } override fun onStop() { super.onStop() disposable.clear() } }
Java
public class ConcertActivity extends AppCompatActivity { private ConcertAdapter adapter = new ConcertAdapter(); private ConcertViewModel viewModel; private CompositeDisposable disposable = new CompositeDisposable(); @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); RecyclerView recyclerView = findViewById(R.id.concert_list); viewModel = new ViewModelProvider(this).get(ConcertViewModel.class); recyclerView.setAdapter(adapter); } @Override protected void onStart() { super.onStart(); disposable.add(viewModel.concertList .subscribe(adapter.submitList(flowableList) )); } @Override protected void onStop() { super.onStop(); disposable.clear(); } }
Kode untuk ConcertDao
dan ConcertAdapter
sama baik untuk
solusi berbasis RxJava2
maupun
LiveData
.
Memberikan masukan
Sampaikan masukan dan ide Anda kepada kami melalui resource berikut:
- Issue tracker
- Laporkan masalah agar kami dapat memperbaiki bug.
Referensi lainnya
Untuk mempelajari Library Paging lebih lanjut, pelajari referensi berikut.
Contoh
Codelab
Video
- Android Jetpack: mengelola daftar tanpa batas dengan RecyclerView dan Paging (Google I/O '18)
- Android Jetpack: Paging
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Bermigrasi ke Paging 3
- Menampilkan daftar yang dibagi-bagi
- Mengumpulkan data yang dibagi-bagi