Composants d'architecture Android Utilisation de la bibliothèque de pagination avec Room

Dans ce tutoriel, je vais vous montrer comment utiliser la bibliothèque de pagination à partir des composants d'architecture Android avec une base de données assortie d'une pièce dans une application Android.. 

Vous apprendrez à utiliser la bibliothèque de radiomessagerie pour charger efficacement des ensembles de données volumineux à partir d'une base de données assortie d'une pièce, offrant ainsi à vos utilisateurs une expérience plus fluide tout en faisant défiler une fenêtre RecyclerView. 

Conditions préalables

Pour pouvoir suivre ce tutoriel, vous aurez besoin de:

  • Android Studio 3.1.3 ou supérieur
  • Kotlin plugin 1.2.51 ou supérieur
  • compréhension de base des composants d’architecture Android (en particulier le Données en direct et base de données des chambres)

Si vous ne connaissez pas les composants de l'architecture, nous vous conseillons vivement de consulter notre impressionnante série consacrée aux composants d'architecture Android de Tin Megali. Assurez-vous d'aller plonger! 

Vous trouverez un exemple de projet pour ce tutoriel sur notre dépôt GitHub afin que vous puissiez suivre facilement.

Quelle est la bibliothèque de pagination?

La bibliothèque de pagination est une autre bibliothèque ajoutée aux composants d'architecture. La bibliothèque permet de gérer efficacement le chargement et l’affichage d’un grand ensemble de données dans RecyclerVoir. Selon les documents officiels:

La bibliothèque de radiomessagerie simplifie le chargement des données progressivement et avec élégance dans votre application. RecyclerVoir.

Si une partie de votre application Android doit afficher un grand ensemble de données à partir d'une source de données locale ou distante, mais n'en afficher qu'une partie à la fois, vous devez envisager d'utiliser la bibliothèque de pagination. Cela aidera à améliorer les performances de votre application.! 

Alors, pourquoi utiliser la bibliothèque de pagination?

Maintenant que vous avez vu une introduction à la bibliothèque de pagination, vous pouvez vous demander pourquoi l’utiliser. Voici quelques raisons pour lesquelles vous devriez envisager de l’utiliser lors du chargement de grands ensembles de données dans un environnement donné. RecyclerVoir

  • Il ne demande pas de données inutiles. Cette bibliothèque ne demande que les données visibles par l'utilisateur, au fur et à mesure que l'utilisateur fait défiler la liste.. 
  • Enregistre la batterie de l'utilisateur et consomme moins de bande passante. Comme il ne demande que les données nécessaires, cela économise certaines ressources de l'appareil. 

Cela ne sera pas efficace lorsque vous travaillerez avec une grande quantité de données, car la source de données sous-jacente récupérera toutes les données, même si seul un sous-ensemble de ces données sera affiché à l'utilisateur. Dans une telle situation, nous devrions envisager de paginer les données. 

1. Créer un projet Android Studio

Lancez votre Android Studio 3 et créez un nouveau projet avec une activité vide appelée Activité principale. Assurez-vous de vérifier Inclure le support Kotlin

2. Ajouter les composants d'architecture

Après avoir créé un nouveau projet, ajoutez les dépendances suivantes dans votre build.gradle. Dans ce tutoriel, nous utilisons la dernière version de la bibliothèque Paging 1.0.1, alors que Room est la version 1.1.1 (à ce jour). 

dépendances implementation fileTree (dir: 'libs', include: ['* .jar'])) implémentation "android.arch.persistence.room:runtime:1.1.1" kapt "android.arch.persistence.room:compiler:1.1 .1 "implementation" android.arch.paging: runtime: 1.0.1 "implémentation" com.android.support:recyclerview-v7:27.1.1 "

Ces artefacts sont disponibles sur le référentiel Maven de Google.. 

allprojects repositories google () jcenter ()

En ajoutant les dépendances, nous avons appris à Gradle comment trouver la bibliothèque. N'oubliez pas de synchroniser votre projet après les avoir ajoutés. 

3. Créer l'entité

Créer une nouvelle classe de données Kotlin La personne. Par souci de simplicité, notre La personne L'entité n'a que deux champs:

  • un identifiant unique (identifiant)
  • le nom de la personne (prénom)

En outre, inclure un toString ( méthode qui retourne simplement le prénom

import android.arch.persistence.room.Entity import android.arch.persistence.room.PrimaryKey @Entity (tableName = "personnes") Classe de données person (@PrimaryKey id id: String, nom id: String) remplace fun toString ( ) = nom

4. Créez le DAO

Comme vous le savez, pour que nous puissions accéder aux données de notre application avec la bibliothèque Room, nous avons besoin d'objets DAO. Dans notre propre cas, nous avons créé un PersonDao

importer android.arch.lifecycle.LiveData importer android.arch.paging.DataSource importer android.arch.persistence.room.Dao importer android.arch.persistence.room.Delete importer android.arch.persistence.room.Insert importer android.arch .persistence.room.Query @Dao interface PersonDao @Query ("SELECT * FROM personnes") fun getAll (): LiveData> @Query ("SELECT * FROM personnes") fun getAllPaged (): DataSource.Factory @Insert fun insertAll (personnes: liste) @Delete fun delete (personne: Personne)

Dans notre PersonDao classe, nous avons deux @Question méthodes. L'un d'eux est Avoir tout(), qui retourne un Données en direct qui détient une liste de La personne objets. L'autre est getAllPaged (), qui retourne un DataSource.Factory

Selon les documents officiels, le La source de données la classe est la:

Classe de base pour le chargement de pages de données d'instantané dans un PagedList.

UNE PagedList est un genre particulier de liste pour afficher des données paginées dans Android: 

UNE PagedList est une liste qui charge ses données en morceaux (pages) depuis un La source de données. Les articles peuvent être consultés avec get (int), et le chargement ultérieur peut être déclenché avec loadAround (int).

Nous avons appelé le Usine méthode statique dans le La source de données class, qui sert de fabrique (créer des objets sans avoir à spécifier la classe exacte de l’objet qui sera créé) pour le La source de données. Cette méthode statique prend en deux types de données:

  • La clé qui identifie les éléments dans La source de données. Notez que pour une requête de salle, les pages sont numérotées. Nous utilisons donc Entier comme type d'identifiant de page. Il est possible d'avoir des pages "à clé" en utilisant la bibliothèque de pagination, mais Room ne propose pas cela. 
  • Le type d’éléments ou d’entités (POJO) de la liste chargée par le La source de donnéess.

5. Créer la base de données

Voici ce que notre classe de base de données Room AppDatabase ressemble à: 

importer android.arch.persistence.db.SupportSQLiteDatabase importer android.arch.persistence.room.Database importer android.arch.persistence.room.Room importer android.arch.persistence.room.RoomDatabase importer android.content.Context import androidx.work .OneTimeWorkRequestBuilder import androidx.work.WorkManager import com.chikeandroid.pagingtutsplus.utils.DATABASE_NAME import com.chikeandroid.pagingtutsplus.workers.SeedDatabaseWorker @Database (entités = [Personne :: Classe, personne], version = 1, exportSema AppDatabase: RoomDatabase () abstract fun personDao (): Objet compagnon PersonDao // Pour l'instanciation Singleton @Volatile private var instance: AppDatabase? = null fun getInstance (contexte: contexte): AppDatabase instance de retour?: synchronized (this) instance?: buildDatabase (contexte) .also instance = it amusement privé buildDatabase (contexte: Contexte): AppDatabase salle de retour .databaseBuilder (contexte, AppDatabase :: class.java, DATABASE_NAME) .addCallback (object: RoomDatabase.Callback () substitue l'amusement onCreate (db: SupportSQLiteDatabase) super.onCreate (db) val request = OneTimeWorkRequestBuilder() .build () WorkManager.getInstance ()?. enqueue (request)) .build ()

Ici, nous avons créé une instance unique de notre base de données et pré-remplie avec des données à l'aide de la nouvelle API WorkManager. Notez que les données pré-remplies sont juste une liste de 1 000 noms (plonger dans l'exemple de code source fourni pour en savoir plus).. 

6. Création du ViewModel

Pour que notre interface utilisateur stocke, observe et serve les données de manière consciente du cycle de vie, nous avons besoin d’un ViewModel. Notre PersonnesVueModèle, qui étend la AndroidViewModel classe, va fonctionner comme notre ViewModel

import android.app.Application import android.arch.lifecycle.AndroidViewModel import android.arch.lifecycle.LiveData import android.arch.paging.DataSource import android.arch.paging.LivePagedListBuilder import android.arch.paging.Paging.PagedList import com.chikeandroid .pagingtutsplus.data.AppDatabase importer com.chikeandroid.pagingtutsplus.data.Person, classe PersonsViewModel, constructeur (application: Application): AndroidViewModel (application) private var personsLiveData: LiveData> init val factory: DataSource.Factory = AppDatabase.getInstance (getApplication ()). PersonDao (). GetAllPaged () val pagedListBuilder: LivePagedListBuilder = LivePagedListBuilder(usine, 50) personnesLiveData = pagedListBuilder.build () fun getPersonsLiveData () = personnesLiveData

Dans cette classe, nous avons un seul champ appelé personnesDonnéesDonnées. Ce champ est simplement un Données en direct qui détient un PagedList de La personne objets. Parce que c'est un Données en direct, notre interface utilisateur (la Activité ou Fragment) va observer ces données en appelant la méthode getter getPersonsLiveData ()

Nous avons initialisé personnesDonnéesDonnées à l'intérieur de init bloc. À l'intérieur de ce bloc, nous obtenons le DataSource.Factory en appelant le AppDatabase singleton pour le PersonDao objet. Lorsque nous obtenons cet objet, nous appelons getAllPaged ()

Nous créons ensuite un LivePagedListBuilder. Voici ce que dit la documentation officielle sur un LivePagedListBuilder

Constructeur pour Données en direct, Donné un DataSource.Factory et un PagedList.Config.

Nous fournissons à son constructeur un DataSource.Factory comme premier argument et la taille de la page comme second argument (dans notre cas, la taille de la page sera 50). En règle générale, vous devez choisir une taille supérieure au nombre maximal que vous pouvez afficher en une fois pour l'utilisateur. En fin de compte, nous appelons construire() pour construire et nous rendre un Données en direct

7. Création du PagedListAdapter

Pour montrer notre PagedList données dans un RecyclerVoir, nous avons besoin d'un PagedListAdapter. Voici une définition claire de cette classe de la documentation officielle:

RecyclerView.Adapter classe de base pour la présentation de données paginées à partir de PagedLists dans un RecyclerVoir.

Alors on crée un PersonAdapter qui s'étend PagedListAdapter.

import android.arch.paging.PagedListAdapter import android.content.Context import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import com .chikeandroid.pagingtutsplus.R importer com.chikeandroid.pagingtutsplus.data.Person importer kotlinx.android.synthetic.main.item_person.view. * class PersonAdapter (contexte de val: Contexte): PagedListAdapter(PersonDiffCallback ()) substitue l'amusement onBindViewHolder (holderPerson: PersonViewHolder, position: Int) var person = getItem (position) if (personne == null) holderPerson.clear () autre holderPerson.bind (personne) Remplacez l'amusement onCreateViewHolder (parent: ViewGroup, viewType: Int): PersonViewHolder return PersonViewHolder (LayoutInflater.from (context). .inflate (R.layout.item_person, parent, false)) class PersonViewHolder (vue: vue): RecyclerView.ViewHolder (vue) var tvName: TextView = view.name fun bind (personne: Personne) tvName.text = personne.name fun clear () tvName.text = null

PagedListAdapter est utilisé comme toute autre sous-classe de RecyclerView.Adapter. En d'autres termes, vous devez implémenter les méthodes onCreateViewHolder () et onBindViewHolder ()

Pour étendre le PagedListAdapter classe abstraite, vous devrez fournir dans son constructeur le type de PageLists (cela devrait être une ancienne classe Java simple: un POJO) et aussi une classe qui étend le ViewHolder qui sera utilisé par l'adaptateur. Dans notre cas, nous lui avons donné La personne et PersonViewHolder comme premier et deuxième argument respectivement. 

Notez que PagedListAdapter exige que vous passiez un DiffUtil.ItemCallback au PageListAdapter constructeur. DiffUtil est un RecyclerVoir classe utilitaire capable de calculer la différence entre deux listes et de générer une liste d'opérations de mise à jour qui convertit la première liste en une seconde. ItemCallback est une classe statique abstraite intérieure (à l'intérieur DiffUtil) utilisé pour calculer le diff entre deux éléments non nuls d'une liste. 

Plus précisément, nous fournissons PersonDiffCallback à notre PagedListAdapter constructeur. 

import android.support.v7.util.DiffUtil import com.chikeandroid.pagingtutsplus.data.Person classe PersonDiffCallback: DiffUtil.ItemCallback() redéfinit fun areItemsTheSame (oldItem: Person, newItem: Personne): Boolean return oldItem.id == newItem.id annule fun areContentsTheSame (oldItem: Personne ?, newItem: Personne?): Boolean retour oldItem == newItem 

Parce que nous mettons en œuvre DiffUtil.ItemCallback, nous devons mettre en œuvre deux méthodes: areItemsTheSame () et areContentsTheSame ()

  • areItemsTheSame est appelé pour vérifier si deux objets représentent le même élément. Par exemple, si vos éléments ont des identifiants uniques, cette méthode doit vérifier leur égalité d'identifiant. Cette méthode retourne vrai si les deux éléments représentent le même objet ou faux si elles sont différentes.
  • areContentsTheSame est appelé pour vérifier si deux éléments ont les mêmes données. Cette méthode retourne vrai si le contenu des articles est identique ou faux si elles sont différentes.

Notre PersonViewHolder classe intérieure est juste un typique RecyclerView.ViewHolder. Il est responsable de la liaison des données nécessaires de notre modèle dans les widgets pour une ligne de notre liste.. 

classe PersonAdapter (val contexte: contexte): PagedListAdapter(PersonDiffCallback ()) //… class PersonViewHolder (vue: vue): RecyclerView.ViewHolder (vue) var nomVue: TextView = vue.nom fun lier (personne: Personne) nomNom.text = personne.nom amusement clear () tvName.text = null

8. Affichage du résultat

Dans notre onCreate () de nôtre Activité principale, nous avons simplement fait ce qui suit:

  • initialiser notre voirModèle champ utilisant la classe d'utilitaire ViewModelProviders
  • créer une instance de PersonAdapter
  • configurer notre RecyclerVoir
  • lier le PersonAdapter au RecyclerVoir
  • observer l Données en direct et soumettre le PagedList objets sur le PersonAdapter en invoquant submitList ()
importer android.arch.lifecycle.Observer importer android.arch.lifecycle.ViewModelProviders importer android.os.Bundle importer android.support.v7.app.AppCompatActivity importer android.support.v7.widget.Recycler.import.r. .PersonAdapter import com.chikeandroid.pagingtutsplus.viewmodels.PersonsViewModel classe MainActivity: AppCompatActivity () private tardive viewPodel: SubjectViewModel fun onCreate (savedInstanceState: Bundle?) = ViewModelProviders.of (this) .get (PersonsViewModel :: class.java) val adaptateur = PersonAdapter (this) findViewById(R.id.name_list) .adapter = adaptateur subscribeUi (adaptateur) amusement privé subscribeUi (adaptateur: PersonAdapter) viewModel.getPersonLiveData (). Observe (this, Observateur noms -> if (noms! = Null) adapter.submitList (des noms) )  

Enfin, lorsque vous exécutez l'application, voici le résultat:

Lors du défilement, Room est en mesure de prévenir les lacunes en chargeant 50 éléments à la fois et en les mettant à la disposition de notre PersonAdapter, qui est une sous-classe de PagingListAdapter. Mais notez que toutes les sources de données ne seront pas chargées rapidement. La vitesse de chargement dépend également de la puissance de traitement de l'appareil Android.. 

9. Intégration avec RxJava

Si vous utilisez ou souhaitez utiliser RxJava dans votre projet, la bibliothèque de pagination inclut un autre artefact utile: RxPagedListBuilder. Vous utilisez cet artefact au lieu de LivePagedListBuilder pour le support RxJava. 

Vous créez simplement une instance de RxPagedListBuilder, fournir les mêmes arguments que vous le feriez pour LivePagedListBuilder-la DataSource.Factory et la taille de la page. Vous appelez alors buildObservable () ou buildFlowable () renvoyer un Observable ou Fluide pour votre PagedList respectivement. 

Fournir explicitement le Planificateur pour le travail de chargement de données, vous appelez la méthode setter setFetchScheduler (). Fournir également le Planificateur pour fournir le résultat (par exemple. AndroidSchedulers.mainThread ()), appelez simplement setNotifyScheduler (). Par défaut, setNotifyScheduler () par défaut, le fil de l'interface utilisateur, setFetchScheduler () par défaut, le pool de threads d'E / S. 

Conclusion

Dans ce didacticiel, vous avez appris à utiliser facilement le composant de pagination à partir des composants d'architecture Android (qui font partie d'Android Jetpack) avec Room. Cela nous aide à charger efficacement de grands ensembles de données à partir de la base de données locale pour permettre une expérience utilisateur plus fluide tout en faisant défiler une liste dans la liste. RecyclerVoir

Je recommande fortement de consulter la documentation officielle pour en savoir plus sur la bibliothèque de pagination dans Android.