Mengumpulkan data yang di-page

Dokumen ini dibuat berdasarkan ringkasan Library Paging, yang membahas cara menyesuaikan solusi pemuatan data aplikasi untuk memenuhi kebutuhan arsitektur aplikasi Anda.

Membuat daftar yang dapat diamati

Biasanya, kode UI Anda mengamati objek LiveData<PagedList> (atau, jika Anda menggunakan RxJava2, objek Flowable<PagedList> atau Observable<PagedList>), yang berada di ViewModel aplikasi Anda. Objek yang dapat diamati ini membentuk koneksi antara presentasi dan konten data daftar aplikasi Anda.

Untuk membuat salah satu objek PagedList yang dapat diamati ini, teruskan instance DataSource.Factory ke objek LivePagedListBuilder atau RxPagedListBuilder. Objek DataSource memuat halaman untuk satu PagedList. Class factory membuat instance baru PagedList sebagai respons terhadap update konten, seperti pembatalan tabel database dan pemuatan ulang jaringan. Library persistensi Room dapat menyediakan objek DataSource.Factory untuk Anda, atau Anda dapat membuatnya sendiri.

Cuplikan kode berikut menunjukkan cara membuat instance baru LiveData<PagedList> di class ViewModel aplikasi Anda menggunakan kemampuan membangun DataSource.Factory Room:

ConcertDao

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>
}

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();
}

ConcertViewModel

Kotlin

// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
       concertDao.concertsByDate()

val concertList = myConcertDataSource.toLiveData(pageSize = 50)

Java

// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
       concertDao.concertsByDate();

LiveData<PagedList<Concert>> concertList =
        LivePagedListBuilder(myConcertDataSource, /* page size */ 50).build();

Menentukan konfigurasi paging Anda sendiri

Untuk mengonfigurasi LiveData<PagedList> lebih lanjut untuk kasus lanjutan, Anda juga dapat menentukan konfigurasi paging Anda sendiri. Secara khusus, Anda dapat menentukan atribut berikut:

  • Ukuran halaman: Jumlah item di setiap halaman.
  • Jarak prefetch: Dengan item terakhir yang terlihat di UI aplikasi, jumlah item di luar item terakhir ini yang harus dicoba diambil oleh Library Paging terlebih dahulu. Nilai ini harus beberapa kali lebih besar dari ukuran halaman.
  • Keberadaan placeholder: Menentukan apakah UI menampilkan placeholder untuk item daftar yang belum selesai dimuat. Untuk pembahasan tentang manfaat dan kelemahan penggunaan placeholder, pelajari cara Menyediakan placeholder di UI Anda.

Jika Anda ingin lebih mengontrol kapan Library Paging memuat daftar dari database aplikasi, teruskan objek Executor kustom ke LivePagedListBuilder, seperti yang ditunjukkan dalam cuplikan kode berikut:

ConcertViewModel

Kotlin

val myPagingConfig = Config(
        pageSize = 50,
        prefetchDistance = 150,
        enablePlaceholders = true
)

// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
        concertDao.concertsByDate()

val concertList = myConcertDataSource.toLiveData(
        pagingConfig = myPagingConfig,
        fetchExecutor = myExecutor
)

Java

PagedList.Config myPagingConfig = new PagedList.Config.Builder()
        .setPageSize(50)
        .setPrefetchDistance(150)
        .setEnablePlaceholders(true)
        .build();

// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
        concertDao.concertsByDate();

LiveData<PagedList<Concert>> concertList =
        new LivePagedListBuilder<>(myConcertDataSource, myPagingConfig)
            .setFetchExecutor(myExecutor)
            .build();

Memilih jenis sumber data yang benar

Penting untuk terhubung ke sumber data yang menangani struktur data sumber Anda paling baik:

  • Gunakan PageKeyedDataSource jika halaman yang Anda muat menyematkan kunci berikutnya atau sebelumnya. Misalnya, jika Anda mengambil postingan media sosial dari jaringan, Anda mungkin perlu meneruskan token nextPage dari satu pemuatan ke pemuatan berikutnya.
  • Gunakan ItemKeyedDataSource jika Anda perlu menggunakan data dari item N untuk mengambil item N+1. Misalnya, jika Anda mengambil komentar berangkai untuk aplikasi diskusi, Anda mungkin perlu meneruskan ID komentar terakhir untuk mendapatkan konten komentar berikutnya.
  • Gunakan PositionalDataSource jika Anda perlu mengambil halaman data dari lokasi mana pun yang Anda pilih di datastore. Class ini mendukung permintaan kumpulan item data yang dimulai dari lokasi mana pun yang Anda pilih. Misalnya, permintaan mungkin menampilkan 50 item data yang dimulai dengan lokasi 1500.

Memberi tahu jika data tidak valid

Saat menggunakan Library Paging, lapisan data bertanggung jawab memberi tahu lapisan lain di aplikasi Anda saat tabel atau baris menjadi usang. Untuk melakukannya, panggil invalidate() dari class DataSource yang telah Anda pilih untuk aplikasi Anda.

Membuat sumber data Anda sendiri

Jika menggunakan solusi data lokal kustom, atau jika Anda memuat data langsung dari jaringan, Anda dapat mengimplementasikan salah satu subclass DataSource. Cuplikan kode berikut menunjukkan sumber data yang dihitung dari waktu mulai konser tertentu:

Kotlin

class ConcertTimeDataSource() :
        ItemKeyedDataSource<Date, Concert>() {
    override fun getKey(item: Concert) = item.startTime

    override fun loadInitial(
            params: LoadInitialParams<Date>,
            callback: LoadInitialCallback<Concert>) {
        val items = fetchItems(params.requestedInitialKey,
                params.requestedLoadSize)
        callback.onResult(items)
    }

    override fun loadAfter(
            params: LoadParams<Date>,
            callback: LoadCallback<Concert>) {
        val items = fetchItemsAfter(
            date = params.key,
            limit = params.requestedLoadSize)
        callback.onResult(items)
    }
}

Java

public class ConcertTimeDataSource
        extends ItemKeyedDataSource<Date, Concert> {
    @NonNull
    @Override
    public Date getKey(@NonNull Concert item) {
        return item.getStartTime();
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams<Date> params,
            @NonNull LoadInitialCallback<Concert> callback) {
        List<Concert> items =
            fetchItems(params.key, params.requestedLoadSize);
        callback.onResult(items);
    }

    @Override
    public void loadAfter(@NonNull LoadParams<Date> params,
            @NonNull LoadCallback<Concert> callback) {
        List<Concert> items =
            fetchItemsAfter(params.key, params.requestedLoadSize);
        callback.onResult(items);
    }
}

Kemudian, Anda dapat memuat data yang disesuaikan ini ke dalam objek PagedList dengan membuat subclass konkret dari DataSource.Factory. Cuplikan kode berikut menunjukkan cara membuat instance baru sumber data kustom yang ditentukan dalam cuplikan kode sebelumnya:

Kotlin

class ConcertTimeDataSourceFactory :
        DataSource.Factory<Date, Concert>() {
    val sourceLiveData = MutableLiveData<ConcertTimeDataSource>()
    var latestSource: ConcertDataSource?
    override fun create(): DataSource<Date, Concert> {
        latestSource = ConcertTimeDataSource()
        sourceLiveData.postValue(latestSource)
        return latestSource
    }
}

Java

public class ConcertTimeDataSourceFactory
        extends DataSource.Factory<Date, Concert> {
    private MutableLiveData<ConcertTimeDataSource> sourceLiveData =
            new MutableLiveData<>();

    private ConcertDataSource latestSource;

    @Override
    public DataSource<Date, Concert> create() {
        latestSource = new ConcertTimeDataSource();
        sourceLiveData.postValue(latestSource);
        return latestSource;
    }
}

Mempertimbangkan cara kerja pembaruan konten

Saat Anda membuat objek PagedList yang dapat diamati, pertimbangkan cara kerja pembaruan konten. Jika Anda memuat data langsung dari database Room, update akan otomatis dikirim ke UI aplikasi Anda.

Saat menggunakan API jaringan halaman, Anda biasanya memiliki interaksi pengguna, seperti "geser untuk memuat ulang", yang berfungsi sebagai sinyal untuk membatalkan validasi DataSource yang terakhir Anda gunakan. Kemudian, Anda meminta instance baru dari sumber data tersebut. Cuplikan kode berikut menunjukkan perilaku ini:

Kotlin

class ConcertActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
        concertTimeViewModel.refreshState.observe(this, Observer {
            // Shows one possible way of triggering a refresh operation.
            swipeRefreshLayout.isRefreshing =
                    it == MyNetworkState.LOADING
        })
        swipeRefreshLayout.setOnRefreshListener {
            concertTimeViewModel.invalidateDataSource()
        }
    }
}

class ConcertTimeViewModel(firstConcertStartTime: Date) : ViewModel() {
    val dataSourceFactory = ConcertTimeDataSourceFactory(firstConcertStartTime)
    val concertList: LiveData<PagedList<Concert>> =
            dataSourceFactory.toLiveData(
                pageSize = 50,
                fetchExecutor = myExecutor
            )

    fun invalidateDataSource() =
            dataSourceFactory.sourceLiveData.value?.invalidate()
}

Java

public class ConcertActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        // ...
        viewModel.getRefreshState()
                .observe(this, new Observer<NetworkState>() {
            // Shows one possible way of triggering a refresh operation.
            @Override
            public void onChanged(@Nullable MyNetworkState networkState) {
                swipeRefreshLayout.isRefreshing =
                        networkState == MyNetworkState.LOADING;
            }
        };

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshListener() {
            @Override
            public void onRefresh() {
                viewModel.invalidateDataSource();
            }
        });
    }
}

public class ConcertTimeViewModel extends ViewModel {
    private LiveData<PagedList<Concert>> concertList;
    private DataSource<Date, Concert> mostRecentDataSource;

    public ConcertTimeViewModel(Date firstConcertStartTime) {
        ConcertTimeDataSourceFactory dataSourceFactory =
                new ConcertTimeDataSourceFactory(firstConcertStartTime);
        mostRecentDataSource = dataSourceFactory.create();
        concertList = new LivePagedListBuilder<>(dataSourceFactory, 50)
                .setFetchExecutor(myExecutor)
                .build();
    }

    public void invalidateDataSource() {
        mostRecentDataSource.invalidate();
    }
}

Menyediakan pemetaan data

Library Paging mendukung transformasi berbasis item dan berbasis halaman untuk item yang dimuat oleh DataSource.

Dalam cuplikan kode berikut, gabungan antara nama konser dan tanggal konser dipetakan ke satu string yang berisi nama dan tanggal:

Kotlin

class ConcertViewModel : ViewModel() {
    val concertDescriptions : LiveData<PagedList<String>>
        init {
            val concerts = database.allConcertsFactory()
                    .map { "${it.name} - ${it.date}" }
                    .toLiveData(pageSize = 50)
        }
}

Java

public class ConcertViewModel extends ViewModel {
    private LiveData<PagedList<String>> concertDescriptions;

    public ConcertViewModel(MyDatabase database) {
        DataSource.Factory<Integer, Concert> factory =
                database.allConcertsFactory().map(concert ->
                    concert.getName() + "-" + concert.getDate());
        concertDescriptions = new LivePagedListBuilder<>(
            factory, /* page size */ 50).build();
    }
}

Cara ini dapat berguna jika Anda ingin menggabung, mengonversi, atau menyiapkan item setelah dimuat. Karena pekerjaan ini dilakukan di eksekutor pengambilan, Anda dapat melakukan pekerjaan yang berpotensi mahal, seperti membaca dari disk atau membuat kueri database terpisah.

Berikan 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, lihat referensi berikut.

Contoh

Codelab

Video