Seitendaten erfassen

Dieser Leitfaden baut auf der Übersicht über die Paging-Bibliothek auf. Hier wird erläutert, wie Sie die Lösung zum Laden von Daten Ihrer Anwendung an die Architekturanforderungen Ihrer Anwendung anpassen können.

Eine beobachtbare Liste erstellen

In der Regel beobachtet der UI-Code ein LiveData<PagedList>-Objekt (oder, wenn Sie RxJava2 verwenden, ein Flowable<PagedList>- oder Observable<PagedList>-Objekt), das sich im ViewModel Ihrer Anwendung befindet. Dieses beobachtbare Objekt stellt eine Verbindung zwischen der Präsentation und den Inhalten der Listendaten Ihrer App her.

Um eines dieser beobachtbaren PagedList-Objekte zu erstellen, übergeben Sie eine Instanz von DataSource.Factory an ein LivePagedListBuilder- oder RxPagedListBuilder-Objekt. Ein DataSource-Objekt lädt Seiten für eine einzelne PagedList. Die Factory-Klasse erstellt neue Instanzen von PagedList als Reaktion auf Inhaltsaktualisierungen wie die Entwertung von Datenbanktabellen und Netzwerkaktualisierungen. Die Room Persistence Library kann Ihnen DataSource.Factory-Objekte zur Verfügung stellen. Sie können aber auch eigene Objekte erstellen.

Das folgende Code-Snippet zeigt, wie Sie mithilfe der Gebäudefunktionen DataSource.Factory des Raums eine neue Instanz von LiveData<PagedList> in der Klasse ViewModel Ihrer App erstellen:

KonzertDao

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

Konzertansichtsmodell

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

Eigene Pagingkonfiguration definieren

Wenn Sie eine LiveData<PagedList> für komplexere Fälle konfigurieren möchten, können Sie auch eine eigene Seitenkonfiguration definieren. Insbesondere können Sie die folgenden Attribute definieren:

  • Seitengröße:Die Anzahl der Elemente auf jeder Seite.
  • Prefetch-Entfernung:Unter Berücksichtigung des letzten sichtbaren Elements in der Benutzeroberfläche einer App wird die Anzahl der Elemente, die über dieses letzte Element hinausgehen, angegeben, die die Paging Library im Voraus abrufen sollte. Dieser Wert sollte um ein Vielfaches größer als die Seitengröße sein.
  • Platzhaltervorhandensein:Legt fest, ob auf der Benutzeroberfläche Platzhalter für Listenelemente angezeigt werden, die noch nicht vollständig geladen wurden. Informationen zu den Vor- und Nachteilen von Platzhaltern finden Sie unter Platzhalter auf Ihrer Benutzeroberfläche bereitstellen.

Wenn Sie genauer steuern möchten, wann die Paging Library eine Liste aus der Datenbank Ihrer Anwendung lädt, können Sie ein benutzerdefiniertes Executor-Objekt an LivePagedListBuilder übergeben, wie im folgenden Code-Snippet gezeigt:

Konzertansichtsmodell

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

Den richtigen Typ der Datenquelle auswählen

Es ist wichtig, eine Verbindung zu der Datenquelle herzustellen, die die Struktur Ihrer Quelldaten am besten handhabt:

  • Verwenden Sie PageKeyedDataSource, wenn auf Seiten, die Sie laden, Schlüssel vom Typ „Nächste/vorherige“ eingebettet werden. Wenn Sie beispielsweise Beiträge in sozialen Medien aus dem Netzwerk abrufen, müssen Sie möglicherweise ein nextPage-Token von einem Ladevorgang an einen nachfolgenden Ladevorgang übergeben.
  • Verwenden Sie ItemKeyedDataSource, wenn Sie Daten aus Element N zum Abrufen des Elements N+1 verwenden müssen. Wenn Sie beispielsweise Kommentare mit Unterhaltungsthreads für eine Diskussions-App abrufen, müssen Sie möglicherweise die ID des letzten Kommentars übergeben, um den Inhalt des nächsten Kommentars abzurufen.
  • Verwenden Sie PositionalDataSource, wenn Sie Seiten mit Daten von einem beliebigen Speicherort in Ihrem Datenspeicher abrufen müssen. Diese Klasse unterstützt das Anfordern eines Satzes von Datenelementen ab dem von Ihnen ausgewählten Speicherort. Die Anfrage könnte beispielsweise die 50 Datenelemente zurückgeben, die mit Position 1500 beginnen.

Benachrichtigen, wenn Daten ungültig sind

Wenn Sie die Paging Library verwenden, muss die Datenschicht die anderen Ebenen der Anwendung benachrichtigen, wenn eine Tabelle oder Zeile veraltet ist. Rufen Sie dazu invalidate() aus der Klasse DataSource auf, die Sie für Ihre Anwendung ausgewählt haben.

Eigene Datenquellen erstellen

Wenn Sie eine benutzerdefinierte lokale Datenlösung verwenden oder Daten direkt aus einem Netzwerk laden, können Sie eine der abgeleiteten DataSource-Klassen implementieren. Das folgende Code-Snippet zeigt eine Datenquelle, die auf der Startzeit eines bestimmten Konzerts basiert:

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

Sie können diese benutzerdefinierten Daten dann in PagedList-Objekte laden, indem Sie eine konkrete abgeleitete Klasse von DataSource.Factory erstellen. Das folgende Code-Snippet zeigt, wie neue Instanzen der benutzerdefinierten Datenquelle generiert werden, die im vorherigen Code-Snippet definiert wurde:

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

Überdenken, wie Inhaltsaktualisierungen funktionieren

Berücksichtigen Sie beim Erstellen von beobachtbaren PagedList-Objekten, wie Inhaltsaktualisierungen funktionieren. Wenn Sie Daten direkt aus einer Raumdatenbank laden, werden Aktualisierungen automatisch an die Benutzeroberfläche Ihrer App übertragen.

Wenn Sie eine Netzwerk-API mit Seiteninformationen verwenden, erfolgt in der Regel eine Nutzerinteraktion wie „Zum Aktualisieren wischen“, um die zuletzt verwendete DataSource zu ungültig zu machen. Anschließend fordern Sie eine neue Instanz dieser Datenquelle an. Das folgende Code-Snippet veranschaulicht dieses Verhalten:

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

Datenzuordnung bereitstellen

Die Auslagerungsbibliothek unterstützt element- und seitenbasierte Transformationen von Elementen, die von einem DataSource geladen werden.

Im folgenden Code-Snippet wird eine Kombination aus Konzertname und Konzertdatum einem einzelnen String zugeordnet, der sowohl den Namen als auch das Datum enthält:

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

Dies kann nützlich sein, wenn Sie Elemente nach dem Laden zusammenfassen, konvertieren oder vorbereiten möchten. Da diese Arbeit mit dem Abruf-Executor ausgeführt wird, können möglicherweise kostspielige Aufgaben wie das Lesen von der Festplatte oder das Abfragen einer separaten Datenbank ausgeführt werden.

Feedback geben

Teilen Sie uns Ihr Feedback und Ihre Ideen über diese Ressourcen mit:

Problemverfolgung
Melden Sie Probleme, damit wir sie beheben können.

Weitere Informationen

Weitere Informationen zur Paging Library finden Sie in den folgenden Ressourcen.

Produktproben

Codelabs

Videos