Componenti di architettura Android utilizzo della libreria di paging con sala

In questo tutorial, ti mostrerò come utilizzare la libreria di paging dai componenti di architettura Android con un database supportato da room in un'app Android. 

Scoprirai come utilizzare la libreria Paging per caricare in modo efficiente set di dati di grandi dimensioni da un database di Room-backed, dando ai tuoi utenti un'esperienza più fluida mentre scorri in un RecyclerView. 

Prerequisiti

Per poter seguire questo tutorial, avrai bisogno di:

  • Android Studio 3.1.3 o versioni successive
  • Plugin Kotlin 1.2.51 o superiore
  • una conoscenza di base dei componenti di architettura Android (in particolare il LiveData e database di camera)

Se non hai imparato a conoscere i componenti dell'architettura, ti consigliamo vivamente di dare un'occhiata alle nostre fantastiche serie su Android Architecture Components di Tin Megali. Assicurati di andare a fare immersioni! 

Un esempio di progetto per questo tutorial può essere trovato sul nostro repository GitHub in modo da poter seguire facilmente.

Cos'è la libreria di cercapersone?

La libreria di paging è un'altra libreria aggiunta ai componenti di architettura. La libreria aiuta a gestire in modo efficiente il caricamento e la visualizzazione di un grande set di dati nel RecyclerView. Secondo i documenti ufficiali:

La libreria di paging semplifica il caricamento dei dati in modo graduale e lineare all'interno dell'app RecyclerView.

Se una qualsiasi parte della tua app Android visualizza un set di dati di grandi dimensioni da un'origine dati locale o remota ma ne visualizza solo una parte alla volta, dovresti prendere in considerazione l'utilizzo della libreria Paging. Ciò contribuirà a migliorare le prestazioni della tua app! 

Quindi perché usare la libreria di paging?

Ora che hai visto un'introduzione alla libreria di Paging, potresti chiedere, perché usarlo? Ecco alcuni motivi per cui dovresti considerare di usarlo per caricare grandi insiemi di dati in a RecyclerView

  • Non richiede dati che non sono necessari. Questa libreria richiede solo dati visibili all'utente, mentre l'utente scorre l'elenco. 
  • Salva la batteria dell'utente e consuma meno larghezza di banda. Poiché richiede solo i dati necessari, consente di risparmiare alcune risorse del dispositivo. 

Non sarà efficiente quando si lavora con una grande quantità di dati, poiché l'origine dati sottostante recupera tutti i dati, anche se solo un sottoinsieme di tali dati verrà visualizzato all'utente. In una situazione del genere, dovremmo prendere in considerazione il paging dei dati. 

1. Creare un progetto per Android Studio

Avvia il tuo Android Studio 3 e crea un nuovo progetto con un'attività vuota chiamata Attività principale. Assicurati di controllare Include il supporto Kotlin

2. Aggiungi i componenti di architettura

Dopo aver creato un nuovo progetto, aggiungi le seguenti dipendenze nel tuo build.gradle. In questo tutorial, stiamo usando l'ultima versione della libreria Paging 1.0.1, mentre Room è la 1.1.1 (al momento della stesura di questo documento). 

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

Questi artefatti sono disponibili nel repository Maven di Google. 

allprojects repositories google () jcenter ()

Aggiungendo le dipendenze, abbiamo insegnato a Gradle come trovare la libreria. Assicurati di ricordare di sincronizzare il progetto dopo averlo aggiunto. 

3. Creare l'entità

Crea una nuova classe di dati di Kotlin Persona. Per semplicità, il nostro Persona entità ha solo due campi:

  • un ID univoco (id)
  • il nome della persona (nome)

Inoltre, includi a accordare( metodo che restituisce semplicemente il nome

import android.arch.persistence.room.Entity import android.arch.persistence.room.PrimaryKey @Entity (tableName = "persons") classe di dati Person (@PrimaryKey val id: String, nome val: String) override fun toString ( ) = nome

4. Creare il DAO

Come sapete, per noi di accedere ai dati della nostra app con la libreria Room, abbiamo bisogno di oggetti di accesso ai dati (DAO). Nel nostro caso, abbiamo creato un PersonDao

import android.arch.lifecycle.LiveData import android.arch.paging.DataSource import android.arch.persistence.room.Dao import android.arch.persistence.room.Delete import android.arch.persistence.room.Insert import android.arch .persistence.room.Query @DaoDao PersonDao @Query ("SELECT * FROM persons") fun getAll (): LiveData> @Query ("SELECT * FROM persons") fun getAllPaged (): DataSource.Factory @Inserisci divertente insertAll (persone: Elenco) @Delete fun delete (person: Person)

Nel nostro PersonDao classe, ne abbiamo due @query metodi. Uno di loro è prendi tutto(), che restituisce a LiveData che contiene un elenco di Persona oggetti. L'altro è getAllPaged (), che restituisce a DataSource.Factory

Secondo i documenti ufficiali, il Fonte di dati la classe è:

Classe base per il caricamento di pagine di dati di istantanee in a PagedList.

UN PagedList è un tipo speciale di Elenco per mostrare i dati pagati in Android: 

UN PagedList è una lista che carica i suoi dati in blocchi (pagine) da a Fonte di dati. Gli oggetti sono accessibili con ottenere (int), e un ulteriore caricamento può essere attivato con loadAround (int).

Abbiamo chiamato il Fabbrica metodo statico nel Fonte di dati class, che funge da factory (creazione di oggetti senza dover specificare la classe esatta dell'oggetto che verrà creato) per il Fonte di dati. Questo metodo statico accetta due tipi di dati:

  • La chiave che identifica gli elementi in Fonte di dati. Nota che per una query Room, le pagine sono numerate, quindi utilizziamo Numero intero come il tipo di identificativo della pagina. È possibile avere pagine "digitate" usando la libreria Paging, ma Room non lo offre al momento. 
  • Il tipo di elementi o entità (POJO) nell'elenco caricato da Fonte di datiS.

5. Creare il database

Ecco cosa è la nostra classe di database Room AppDatabase sembra: 

import android.arch.persistence.db.SupportSQLiteDatabase import android.arch.persistence.room.Database import android.arch.persistence.room.Room import android.arch.persistence.room.RoomDatabase import 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 (entities = [Person :: class], version = 1, exportSchema = false) classe astratta AppDatabase: RoomDatabase () abstract fun personDao (): oggetto companion PersonDao // Per istanza Singleton @Volatile istanza var privata: AppDatabase? = null divertente getInstance (context: Context): AppDatabase return instance?: synchronized (this) instance?: buildDatabase (context) .also instance = it private fun BuildDatabase (context: Context): AppDatabase return Room .databaseBuilder (context, AppDatabase :: class.java, DATABASE_NAME) .addCallback (oggetto: RoomDatabase.Callback () override fun onCreate (db: SupportSQLiteDatabase) super.onCreate (db) val request = OneTimeWorkRequestBuilder() .build () WorkManager.getInstance () ?. enqueue (richiesta)) .build ()

Qui abbiamo creato una singola istanza del nostro database e pre-popolata con i dati utilizzando la nuova API WorkManager. Si noti che i dati pre-compilati sono solo un elenco di 1.000 nomi (immergersi nel codice sorgente di esempio fornito per ulteriori informazioni). 

6. Creazione del ViewModel

Affinché la nostra interfaccia utente memorizzi, osservi e serva i dati in modo consapevole dal punto di vista del ciclo di vita, abbiamo bisogno di un ViewModel. Nostro PersonsViewModel, che estende il AndroidViewModel classe, funzionerà come nostro 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.PagedList import com.chikeandroid .pagingtutsplus.data.AppDatabase import com.chikeandroid.pagingtutsplus.data.Person class PeopleViewModel constructor (application: Application): AndroidViewModel (applicazione) private var personsLiveData: LiveData> init val factory: DataSource.Factory = AppDatabase.getInstance (getApplication ()). PersonDao (). GetAllPaged () val pagedListBuilder: LivePagedListBuilder = LivePagedListBuilder(factory, 50) personsLiveData = pagedListBuilder.build () fun getPersonsLiveData () = personsLiveData

In questa classe, abbiamo un singolo campo chiamato personsLiveData. Questo campo è semplicemente un LiveData che detiene un PagedList di Persona oggetti. Perché questo è un LiveData, la nostra interfaccia utente (il Attività o Frammento) osserverà questi dati chiamando il metodo getter getPersonsLiveData ()

Abbiamo inizializzato personsLiveData dentro il dentro bloccare. All'interno di questo blocco, otteniamo il DataSource.Factory chiamando il AppDatabase singleton per il PersonDao oggetto. Quando otteniamo questo oggetto, chiamiamo getAllPaged ()

Quindi creiamo a LivePagedListBuilder. Ecco cosa dice la documentazione ufficiale su a LivePagedListBuilder

Costruttore per LiveData, dato a DataSource.Factory e a PagedList.Config.

Forniamo il suo costruttore a DataSource.Factory come primo argomento e dimensione della pagina come secondo argomento (nel nostro caso, la dimensione della pagina sarà 50). In genere, è necessario scegliere una dimensione superiore al numero massimo che è possibile visualizzare contemporaneamente all'utente. Alla fine, chiamiamo costruire() costruire e tornare a noi a LiveData

7. Creazione di PagedListAdapter

Per mostrare il nostro PagedList dati in a RecyclerView, abbiamo bisogno di un PagedListAdapter. Ecco una chiara definizione di questa classe dai documenti ufficiali:

RecyclerView.Adapter classe base per la presentazione di dati paginati da PagedLists in a RecyclerView.

Quindi creiamo a PersonAdapter quello si estende PagedListAdapter.

importare android.arch.paging.PagedListAdapter importare android.content.Context importare android.support.v7.widget.RecyclerView importare android.view.LayoutInflater importare android.view.View importare android.view.ViewGroup importare android.widget.TextView import com .chikeandroid.pagingtutsplus.R import com.chikeandroid.pagingtutsplus.data.Person import kotlinx.android.synthetic.main.item_person.view. * class PersonAdapter (val context: Context): PagedListAdapter(PersonDiffCallback ()) override fun onBindViewHolder (holderPerson: PersonViewHolder, position: Int) var person = getItem (posizione) if (person == null) holderPerson.clear () else holderPerson.bind (person) override fun onCreateViewHolder (parent: ViewGroup, viewType: Int): PersonViewHolder return PersonViewHolder (LayoutInflater.from (context) .inflate (R.layout.item_person, parent, false)) class PersonViewHolder (view: View): RecyclerView.ViewHolder (visualizza) var tvName: TextView = view.name fun bind (person: Person) tvName.text = person.name fun clear () tvName.text = null

PagedListAdapter è usato come qualsiasi altra sottoclasse di RecyclerView.Adapter. In altre parole, devi implementare i metodi onCreateViewHolder () e onBindViewHolder ()

Per estendere il PagedListAdapter classe astratta, dovrai fornire, nel suo costruttore, il tipo di PageLists (questa dovrebbe essere una semplice vecchia classe Java: un POJO) e anche una classe che estende il ViewHolder che verrà utilizzato dall'adattatore. Nel nostro caso, l'abbiamo dato Persona e PersonViewHolder come primo e secondo argomento rispettivamente. 

Nota che PagedListAdapter richiede di passarlo a DiffUtil.ItemCallback al PageListAdapter costruttore. DiffUtil è un RecyclerView classe di utilità che può calcolare la differenza tra due elenchi e generare un elenco di operazioni di aggiornamento che converte la prima lista nella seconda. ItemCallback è una classe statica astratta interna (all'interno DiffUtil) utilizzato per calcolare il diff tra due elementi non nulli in un elenco. 

In particolare, forniamo PersonDiffCallback a noi PagedListAdapter costruttore. 

import android.support.v7.util.DiffUtil import com.chikeandroid.pagingtutsplus.data.Person class PersonDiffCallback: DiffUtil.ItemCallback() override fun areItemsTheSame (oldItem: Person, newItem: Person): Boolean return oldItem.id == newItem.id override fun areContentsTheSame (oldItem: Person ?, newItem: Person?): Boolean return oldItem == newItem 

Perché stiamo implementando DiffUtil.ItemCallback, dobbiamo implementare due metodi: areItemsTheSame () e areContentsTheSame ()

  • areItemsTheSame viene chiamato per verificare se due oggetti rappresentano lo stesso oggetto. Ad esempio, se i tuoi articoli hanno ID univoci, questo metodo dovrebbe verificare la loro uguaglianza di id. Questo metodo restituisce vero se i due elementi rappresentano lo stesso oggetto o falso se sono diversi.
  • areContentsTheSame viene chiamato per verificare se due articoli hanno gli stessi dati. Questo metodo restituisce vero se il contenuto degli articoli è lo stesso o falso se sono diversi.

Nostro PersonViewHolder la classe interiore è solo un tipico RecyclerView.ViewHolder. È responsabile del binding dei dati secondo necessità dal nostro modello ai widget per una riga nel nostro elenco. 

class PersonAdapter (val context: Context): PagedListAdapter(PersonDiffCallback ()) // ... class PersonViewHolder (view: View): RecyclerView.ViewHolder (view) var tvName: TextView = view.name fun bind (person: Person) tvName.text = person.name fun clear () tvName.text = null

8. Mostrare il risultato

Nel nostro onCreate () della nostra Attività principale, abbiamo semplicemente fatto quanto segue:

  • inizializzare la nostra ViewModel campo usando la classe di utilità ViewModelProviders
  • creare un'istanza di PersonAdapter
  • configurare il nostro RecyclerView
  • legare il PersonAdapter al RecyclerView
  • osservare il LiveData e inviare il PagedList oggetti oltre al PersonAdapter invocando submitList ()
import android.arch.lifecycle.Observer import android.arch.lifecycle.ViewModelProviders import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.support.v7.widget.RecyclerView import com.chikeandroid.pagingtutsplus.adapter .PersonAdapter import com.chikeandroid.pagingtutsplus.viewmodels.PersonsViewModel class MainActivity: AppCompatActivity () private lateinit var viewModel: PeopleViewModel sovrascrive fun onCreate (savedInstanceState: Bundle?) Super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) viewModel = ViewModelProviders.of (this) .get (PersonsViewModel :: class.java) val adapter = PersonAdapter (this) findViewById(R.id.name_list) .adapter = adapter subscribeUi (adattatore) private fun subscribeUi (adattatore: PersonAdapter) viewModel.getPersonLiveData (). Osserva (this, Observer names -> if (names! = Null) adapter.submitList (nomi))

Infine, quando esegui l'app, ecco il risultato:

Durante lo scorrimento, Room è in grado di prevenire gli spazi vuoti caricando 50 articoli alla volta e rendendoli disponibili per il nostro PersonAdapter, che è una sottoclasse di PagingListAdapter. Ma nota che non tutte le fonti di dati verranno caricate rapidamente. La velocità di caricamento dipende anche dalla potenza di elaborazione del dispositivo Android. 

9. Integrazione con RxJava

Se stai usando o vuoi usare RxJava nel tuo progetto, la libreria di paging include un altro utile artefatto: RxPagedListBuilder. Tu usi questo artefatto invece di LivePagedListBuilder per il supporto RxJava. 

Devi semplicemente creare un'istanza di RxPagedListBuilder, fornendo gli stessi argomenti che vorresti per LivePagedListBuilder-il DataSource.Factory e la dimensione della pagina. Quindi chiama buildObservable () o buildFlowable () restituire un Osservabilefluido per il tuo PagedList rispettivamente. 

Per fornire esplicitamente il Scheduler per il lavoro di caricamento dei dati, si chiama il metodo setter setFetchScheduler (). Fornire anche il Scheduler per fornire il risultato (ad es. AndroidSchedulers.mainThread ()), basta chiamare setNotifyScheduler (). Di default, setNotifyScheduler () il valore predefinito è il thread dell'interfaccia utente, mentre setFetchScheduler () il valore predefinito è il pool di thread I / O. 

Conclusione

In questo tutorial, hai imparato come utilizzare facilmente il componente Paging dai componenti di Android Architecture (che fanno parte di Android Jetpack) con Room. Questo ci aiuta a caricare in modo efficiente set di dati di grandi dimensioni dal database locale per consentire un'esperienza utente più fluida durante lo scorrimento di un elenco in RecyclerView

Consiglio vivamente di consultare la documentazione ufficiale per ulteriori informazioni sulla libreria di Paging su Android.