Mengumpulkan data yang di-page

Panduan 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 dari data daftar aplikasi Anda.

Untuk membuat salah satu objek PagedList yang dapat diamati ini, teruskan instance DataSource.Factory ke LivePagedListBuilder atau objek RxPagedListBuilder. Objek DataSource memuat halaman untuk satu PagedList. Class factory ini membuat instance baru PagedList sebagai respons terhadap pembaruan konten, seperti pembatalan validasi 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 kapabilitas DataSource.Factory-building 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

Guna mengonfigurasi LiveData<PagedList> lebih lanjut untuk kasus-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 pengambilan data: Dengan memperhitungkan item terlihat terakhir di UI aplikasi, jumlah item di luar item terakhir yang harus diambil oleh Library Paging terlebih dahulu. Nilai ini harus beberapa kali lebih besar daripada ukuran halaman.
  • Kehadiran placeholder: Menentukan apakah UI menampilkan placeholder untuk item daftar yang belum selesai dimuat. Untuk pembahasan tentang keuntungan dan kelemahan penggunaan placeholder, pelajari cara Menyediakan placeholder di UI.

Jika menginginkan kontrol lebih besar terkait kapan Library Paging memuat daftar dari database aplikasi Anda, teruskan objek Executor kustom ke LivePagedListBuilder, seperti 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/sebelumnya. Misalnya, jika 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 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 penyimpanan data. Class ini mendukung permintaan serangkaian item data mulai dari lokasi mana pun yang Anda pilih. Misalnya, permintaan ini dapat menampilkan 50 item data yang diawali 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 dipilih untuk aplikasi Anda.

Membuat sumber data Anda sendiri

Jika menggunakan solusi data lokal kustom, atau jika 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);
    }

Selanjutnya, 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 membuat objek PagedList yang dapat diamati, pertimbangkan cara kerja update konten. Jika Anda memuat data secara langsung dari database Room, update akan otomatis dikirim ke UI aplikasi Anda.

Saat menggunakan API jaringan yang di-page, biasanya Anda memiliki interaksi pengguna, seperti "geser untuk memuat ulang", yang berfungsi sebagai sinyal untuk membatalkan validasi DataSource yang terakhir Anda gunakan. Selanjutnya, minta 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, pelajari referensi berikut.

Contoh

Codelab

Video