Paging 2-Bibliothek – Übersicht Teil von Android Jetpack.
Mit der Paging Library können Sie kleine Datenblöcke gleichzeitig laden und anzeigen. Durch das Laden von Teildaten bei Bedarf wird die Nutzung von Netzwerkbandbreite und Systemressourcen reduziert.
In diesem Leitfaden finden Sie mehrere Konzeptbeispiele der Bibliothek sowie eine Übersicht über ihre Funktionsweise. Vollständige Beispiele für die Funktionsweise dieser Bibliothek finden Sie im Codelab und anhand der Beispiele im Abschnitt Zusätzliche Ressourcen.
Einrichten
Fügen Sie der Datei build.gradle
der App die folgenden Abhängigkeiten hinzu, um Paging-Komponenten in Ihre Android-App zu importieren:
Cool
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 }
Bibliotheksarchitektur
In diesem Abschnitt werden die Hauptkomponenten der Paging-Bibliothek beschrieben und gezeigt.
Seitenliste
Die Hauptkomponente der Paging Library ist die PagedList
-Klasse, die Blöcke der App-Daten – sogenannte Seiten – lädt. Wenn mehr Daten benötigt werden, werden sie in das vorhandene PagedList
-Objekt eingefügt. Wenn sich geladene Daten ändern, wird eine neue Instanz von PagedList
von einem LiveData
- oder RxJava2-basierten Objekt an den beobachtbaren Dateninhaber ausgegeben. Wenn PagedList
-Objekte generiert werden, werden deren Inhalte in der UI Ihrer App angezeigt. Dabei werden die Lebenszyklus der UI-Controller berücksichtigt.
Das folgende Code-Snippet zeigt, wie Sie das Ansichtsmodell Ihrer App so konfigurieren können, dass Daten mit einem LiveData
-Inhaber von PagedList
-Objekten geladen und präsentiert werden:
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(); } }
Daten
Jede Instanz von PagedList
lädt einen aktuellen Snapshot der App-Daten aus dem entsprechenden DataSource
-Objekt. Daten fließen vom Back-End oder der Datenbank Ihrer Anwendung zum PagedList
-Objekt.
Im folgenden Beispiel wird die Room Persistence Library verwendet, um die Daten Ihrer Anwendung zu organisieren. Wenn Sie Ihre Daten jedoch auf andere Weise speichern möchten, können Sie auch Ihre eigene Datenquellen-Factory angeben.
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(); }
Weitere Informationen zum Laden von Daten in PagedList
-Objekte finden Sie unter Auslagerungsdaten laden.
UI
Die Klasse PagedList
arbeitet mit einem PagedListAdapter
, um Elemente in einen RecyclerView
zu laden. Diese Klassen arbeiten zusammen, um Inhalte beim Laden abzurufen und anzuzeigen, nicht sichtbare Inhalte vorab abzurufen und Inhaltsänderungen zu animieren.
Weitere Informationen finden Sie unter Seitenierte Listen aufrufen.
Verschiedene Datenarchitekturen unterstützen
Die Paging Library unterstützt die folgenden Datenarchitekturen:
- Sie wird nur von einem Backend-Server bereitgestellt.
- Nur in einer Datenbank auf dem Gerät gespeichert
- Eine Kombination der anderen Quellen, wobei die Datenbank auf dem Gerät als Cache verwendet wird.
Abbildung 1 zeigt, wie Daten in jedem dieser Architekturszenarien fließen. Bei einer reinen Netzwerk- oder reinen Datenbanklösung fließen die Daten direkt an das UI-Modell Ihrer Anwendung. Wenn Sie einen kombinierten Ansatz verwenden, fließen Daten von Ihrem Back-End-Server zu einer On-Device-Datenbank und dann zum UI-Modell Ihrer Anwendung. Ab und zu werden dem Endpunkt jedes Datenflusses keine Daten mehr zum Laden zur Verfügung stehen. Daher fordert er mehr Daten von der Komponente an, die die Daten bereitgestellt hat. Wenn beispielsweise eine Datenbank auf dem Gerät keine Daten mehr hat, fordert sie mehr Daten vom Server an.
Der Rest dieses Abschnitts enthält Empfehlungen zum Konfigurieren der einzelnen Anwendungsfälle für den Datenfluss.
Nur Netzwerk
Verwenden Sie zum Anzeigen von Daten von einem Back-End-Server die synchrone Version der Retrofit API, um Informationen in Ihr eigenes benutzerdefiniertes DataSource
-Objekt zu laden.
Nur Datenbank
Richten Sie Ihr RecyclerView
so ein, dass der lokale Speicher überwacht wird, vorzugsweise mit der Room Persistence Library. Wenn also Daten in die Datenbank Ihrer Anwendung eingefügt oder geändert werden, werden diese Änderungen automatisch in der RecyclerView
wiedergegeben, in der diese Daten angezeigt werden.
Netzwerk und Datenbank
Nachdem Sie mit der Beobachtung der Datenbank begonnen haben, können Sie mit PagedList.BoundaryCallback
überwachen, ob die Datenbank die Daten aufgebraucht hat.
Sie können dann weitere Elemente aus Ihrem Netzwerk abrufen und in die Datenbank einfügen. Wenn Ihre Benutzeroberfläche die Datenbank beobachtet, brauchen Sie nichts weiter zu tun.
Netzwerkfehler beheben
Wenn Sie die Daten, die Sie über die Paging Library anzeigen, über ein Netzwerk abrufen oder durchblättern, ist es wichtig, das Netzwerk nicht immer als „verfügbar“ oder „nicht verfügbar“ zu behandeln, da viele Verbindungen zeitweise oder instabil sind:
- Ein bestimmter Server antwortet möglicherweise nicht auf eine Netzwerkanfrage.
- Das Gerät ist möglicherweise mit einem langsamen oder schwachen Netzwerk verbunden.
Stattdessen sollte Ihre Anwendung jede Anfrage auf Fehler prüfen und in Fällen, in denen das Netzwerk nicht verfügbar ist, so reibungslos wie möglich wiederhergestellt werden. Sie können beispielsweise eine Schaltfläche „Wiederholen“ zur Verfügung stellen, über die Nutzer auswählen können, wenn der Schritt zur Datenaktualisierung nicht funktioniert. Wenn während des Daten-Pagings ein Fehler auftritt, sollten Sie die Paginierungsanfragen am besten automatisch wiederholen.
Vorhandene App aktualisieren
Wenn Ihre Anwendung bereits Daten aus einer Datenbank oder einer Back-End-Quelle verwendet, ist ein direktes Upgrade auf Funktionen der Paging Library möglich. In diesem Abschnitt wird gezeigt, wie Sie eine App mit einem gängigen bereits vorhandenen Design aktualisieren.
Benutzerdefinierte Paging-Lösungen
Wenn Sie benutzerdefinierte Funktionen zum Laden kleiner Teilmengen von Daten aus der Datenquelle Ihrer Anwendung verwenden, können Sie diese Logik durch die Logik aus der PagedList
-Klasse ersetzen. Instanzen von PagedList
bieten integrierte Verbindungen zu gemeinsamen Datenquellen. Diese Instanzen stellen auch Adapter für RecyclerView
-Objekte bereit, die Sie in die UI Ihrer App aufnehmen können.
Daten, die mithilfe von Listen statt von Seiten geladen werden
Wenn Sie eine speicherinterne Liste als unterstützende Datenstruktur für den Adapter der UI verwenden, sollten Sie Datenaktualisierungen mit einer PagedList
-Klasse beobachten, wenn die Anzahl der Elemente in der Liste groß werden kann. Instanzen von PagedList
können entweder LiveData<PagedList>
oder Observable<List>
verwenden, um Datenaktualisierungen an die UI Ihrer Anwendung zu übergeben und so Ladezeiten und Arbeitsspeichernutzung zu minimieren. Noch besser: Wenn Sie in Ihrer App ein List
-Objekt durch ein PagedList
-Objekt ersetzen, sind keine Änderungen an der UI-Struktur der App oder der Logik zur Datenaktualisierung erforderlich.
Verknüpfen Sie mithilfe von CursorAdapter einen Datencursor mit einer Listenansicht.
Deine Anwendung kann ein CursorAdapter
verwenden, um Daten aus einem Cursor
mit einem ListView
zu verknüpfen. In diesem Fall müssen Sie normalerweise von einem ListView
zu einem RecyclerView
als Listen-UI-Container Ihrer Anwendung migrieren und dann die Komponente Cursor
durch Room oder PositionalDataSource
ersetzen, je nachdem, ob Instanzen von Cursor
auf eine SQLite-Datenbank zugreifen.
In einigen Situationen, z. B. beim Arbeiten mit Instanzen von Spinner
, geben Sie nur den Adapter an. Eine Bibliothek übernimmt die in diesen Adapter geladenen Daten und zeigt sie für Sie an. Ändern Sie in diesen Situationen den Typ der Daten des Adapters in LiveData<PagedList>
und verpacken Sie diese Liste dann in ein ArrayAdapter
-Objekt, bevor Sie versuchen, diese Elemente durch eine Bibliotheksklasse in einer UI aufzublähen.
Inhalte mit AsyncListUtil asynchron laden
Wenn Sie AsyncListUtil
-Objekte verwenden, um Informationsgruppen asynchron zu laden und anzuzeigen, können Sie Daten mit der Paginierungsbibliothek einfacher laden:
- Ihre Daten müssen nicht positionalisiert sein. Mit der Paging Library können Sie Daten mithilfe von Schlüsseln, die vom Netzwerk bereitgestellt werden, direkt aus Ihrem Back-End laden.
- Ihre Daten können unglaublich groß sein. Mit der Paging Library können Sie Daten in Seiten laden, bis keine Daten mehr vorhanden sind.
- Sie können Ihre Daten besser im Blick behalten. Die Paging-Bibliothek kann Ihre Daten, die die ViewModel Ihrer App enthält, in einer beobachtbaren Datenstruktur präsentieren.
Datenbankbeispiele
Die folgenden Code-Snippets zeigen verschiedene Möglichkeiten, wie alle Teile zusammenwirken können.
Ausgelagerte Daten mit LiveData beobachten
Das folgende Code-Snippet zeigt, wie alle Teile zusammenwirken. Wenn Konzertereignisse in der Datenbank hinzugefügt, entfernt oder geändert werden, werden die Inhalte im RecyclerView
automatisch und effizient aktualisiert:
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); } }; }
Ausgelagerte Daten mit RxJava2 beobachten
Wenn Sie RxJava2 anstelle von LiveData
verwenden möchten, können Sie stattdessen ein Observable
- oder Flowable
-Objekt erstellen:
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(); } }
Sie können die Beobachtung der Daten dann mit dem Code im folgenden Snippet starten und beenden:
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(); } }
Der Code für ConcertDao
und ConcertAdapter
ist für eine RxJava2-basierte Lösung identisch mit dem für eine LiveData
-basierte Lösung.
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
- Android Jetpack: unendliche Listen mit RecyclerView und Paging verwalten (Google I/O 2018)
- Android Jetpack: Paging
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Migration zu Paging 3
- Listen mit Seitennummern anzeigen
- Ausgelagerte Daten erfassen