Componente arhitecturale Android Folosind Biblioteca de redare cu cameră

În acest tutorial, vă vom arăta cum să utilizați biblioteca Paging din Componentele Android Architecture cu o bază de date bazată pe cameră într-o aplicație Android. 

Veți învăța cum să utilizați biblioteca de pagini pentru a încărca în mod eficient seturi mari de date dintr-o bază de date bazată pe cameră, oferind utilizatorilor o experiență mai bună în timp ce derulați într-un RecyclerView. 

Cerințe preliminare

Pentru a putea urma acest tutorial, veți avea nevoie de:

  • Android Studio 3.1.3 sau o versiune ulterioară
  • Kotlin plugin 1.2.51 sau mai mare
  • o înțelegere de bază a Componentelor Android de arhitectură (în special LiveData și baza de date a camerei)

Dacă nu ați învățat despre componentele de arhitectură, vă recomandăm să consultați seria noastră minunată despre toate componentele Android Architecture by Tin Megali. Asigurați-vă că vă plimbați! 

Un exemplu de proiect pentru acest tutorial poate fi găsit pe replica noastră GitHub, pentru a putea urmări cu ușurință.

Ce este Biblioteca de paging?

Biblioteca de paginare este o altă bibliotecă adăugată componentelor de arhitectură. Biblioteca ajută în mod eficient la încărcarea și afișarea unui set mare de date în RecyclerView. Potrivit documentelor oficiale:

Biblioteca de pagini vă ușurează încărcarea treptată și grațios a datelor în aplicațiile dvs. RecyclerView.

Dacă o parte a aplicației dvs. Android va afișa un set de date mare dintr-o sursă de date locală sau la distanță, dar va afișa doar o parte din ea la un moment dat, atunci ar trebui să luați în considerare utilizarea bibliotecii Paging. Acest lucru vă va ajuta să îmbunătățiți performanța aplicației! 

Deci, de ce folosiți Biblioteca de paging?

Acum că ați văzut o introducere în biblioteca Paging, ați putea întreba, de ce să o utilizați? Iată câteva motive pentru care ar trebui să luați în considerare utilizarea acestuia la încărcarea seturilor de date mari într-un RecyclerView

  • Nu solicită date care nu sunt necesare. Această bibliotecă solicită numai date vizibile pentru utilizator - pe măsură ce utilizatorul revine în listă. 
  • Salvează bateria utilizatorului și consumă mai puțină lățime de bandă. Deoarece cere numai date necesare, acest lucru economisește câteva resurse ale dispozitivului. 

Nu va fi eficient atunci când lucrați cu o cantitate mare de date, deoarece sursa de date subiacentă preia toate datele, deși numai un subset al acestor date va fi afișat utilizatorului. Într-o astfel de situație, ar trebui să luăm în considerare paginarea datelor. 

1. Creați un proiect Android Studio

Activați Android Studio 3 și creați un nou proiect cu o activitate goală numită Activitate principala. Asigurați-vă că ați verificat Includeți suportul Kotlin

2. Adăugați componentele de arhitectură

După ce ați creat un nou proiect, adăugați următoarele dependențe în build.gradle. În acest tutorial, folosim ultima versiune 1.0.1 a bibliotecii Paging, în timp ce Camera este 1.1.1 (începând cu această scriere). 

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

Aceste artefacte sunt disponibile la depozitul Google Maven. 

toate proiectele repositories google () jcenter ()

Prin adăugarea dependențelor, l-am învățat pe Gradle cum să găsească biblioteca. Asigurați-vă că vă amintiți să vă sincronizați proiectul după ce le-ați adăugat. 

3. Creați Entitatea

Creați o nouă clasă de date Kotlin Persoană. Din simplitate, ale noastre Persoană entitatea are doar două domenii:

  • un ID unic (id)
  • numele persoanei (Nume)

În plus, includeți a toString ( care returnează pur și simplu Nume

(nume_primarKey val id: String, nume val: String) suprascris fun toString ( ) = nume

4. Creați DAO

După cum știți, pentru a accesa datele aplicației noastre în biblioteca de cameră, avem nevoie de obiecte de acces la date (DAO). În cazul nostru, am creat o PersonDao

importați android.arch.lifecycle.LiveData importați android.arch.paging.DataSource importați android.arch.persistence.room.Dao import android.arch.persistence.room.Delete de import android.arch.persistence.room.Insert import android.arch .persistence.room.Query @Dao interfață PersonDao @Query ("SELECT * FROM people") distracție getAll (): LiveData> @Query ("SELECT * FROM persons") distracție getAllPaged (): DataSource.Factory @ Introduceți distracție inserați (Persoane: Listă) @ Șterge distracția șterge (persoană: Persoană)

În a noastră PersonDao clasa, avem două @Query metode. Unul dintre ei este ia tot(), care returnează a LiveData care deține o listă de Persoană obiecte. Celălalt este getAllPaged (), care returnează a DataSource.Factory

Conform documentelor oficiale, Sursă de date clasa este:

Clasa de bază pentru încărcarea paginilor de date instantanee într-un PagedList.

A PagedList este un tip special de Listă pentru afișarea datelor pagerate în Android: 

A PagedList este o listă care încarcă datele sale în bucăți (pagini) de la Sursă de date. Elementele pot fi accesate cu get (int), iar încărcarea ulterioară poate fi declanșată cu loadAround (int).

Am sunat Fabrică metoda statică în Sursă de date clasa, care servește ca o fabrică (crearea de obiecte fără a fi nevoie să precizeze clasa exactă a obiectului care va fi creat) pentru Sursă de date. Această metodă statică are două tipuri de date:

  • Cheia care identifică elementele din Sursă de date. Rețineți că pentru o interogare Cameră, paginile sunt numerotate - așa că vom folosi Întreg ca tip de identificator al paginii. Este posibil să aveți pagini "cheie" folosind Biblioteca de pagini, dar Camera nu oferă în prezent. 
  • Tipul de elemente sau entități (POJOs) din lista încărcată de Sursă de dates.

5. Creați baza de date

Iata ce inseamna clasa bazei de date Room AppDatabase se pare ca: 

importați android.arch.persistence.db.SupportSQLiteDatabase import șiroid.arch.persistence.room.Data de import android.arch.persistence.room.Room de import android.arch.persistence.room.RoomDatabase de import android.content.Context import androidx.work .OneTimeWorkRequestBuilder importați androidx.work.WorkManager import com.chikeandroid.pagingtutsplus.utils.DATABASE_NAME import com.chikeandroid.pagingtutsplus.workers.SeedDatabaseWorker @Database (entities = [Person :: class], version = 1, exportSchema = false) clasă abstractă Aplicația bazei de date: RoomDatabase () abstract persona distracțieDao (): Obiect de persoană personalizată Dada // Pentru instanțierea Singleton @Volatile private instance instance: AppDatabase? = null distracție getInstance (context: Context): AppDatabase instanța retur?: sincronizată (aceasta) instance?: buildDatabase (context) .also instance = it .databaseBuilder (context, AppDatabase :: class.java, DATABASE_NAME) .addCallback (obiect: RoomDatabase.Callback () suprascris fun onCreate (db: SuportSQLiteDatabase) super.onCreate (db) val request = OneTimeWorkRequestBuilder()) .build () WorkManager.getInstance () ?. enqueue (cerere)) .build ()

Aici am creat o singură instanță a bazei noastre de date și l-am prefolosit cu date folosind noul WorkManager API. Rețineți că datele pre-populate sunt doar o listă cu 1000 de nume (se aruncă în codul sursă de probă furnizat pentru a afla mai multe). 

6. Crearea modelului ViewModel

Pentru ca UI-ul nostru să stocheze, să observe și să servească date în mod conștient, avem nevoie de a ViewModel. Al nostru PersonsViewModel, care extinde AndroidViewModel clasa, va funcționa ca și noi ViewModel

importați android.app.Application import șiroid.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 clasa PersonsViewModel constructor (aplicatie: aplicatie): AndroidViewModel (aplicatie) private var peopleLiveData: LiveData> init fabrică val: DataSource.Factory = AppDatabase.getInstance (getApplication ()). PersoanăDao (). GetAllPaged () val pagedListBuilder: LivePagedListBuilder = LivePagedListBuilder(fabrică, 50) peopleLiveData = pagedListBuilder.build () distracție getPersonsLiveData () = personsLiveData

În această clasă, avem un singur câmp numit personsLiveData. Acest câmp este pur și simplu a LiveData care deține a PagedList de Persoană obiecte. Pentru că asta este a LiveData, interfața noastră utilizator Activitate sau Fragment) va observa aceste date apelând metoda getter getPersonsLiveData ()

Am inițializat personsLiveData în interiorul init bloc. În interiorul acestui bloc, obținem DataSource.Factory prin apelarea AppDatabase singleton pentru PersonDao obiect. Când obținem acest obiect, sunăm getAllPaged ()

Apoi creăm un LivePagedListBuilder. Iată ce spune în documentația oficială despre a LivePagedListBuilder

Builder pentru LiveData, dat fiind a DataSource.Factory și a PagedList.Config.

Furnizăm constructorul său a DataSource.Factory ca primul argument și dimensiunea paginii ca al doilea argument (în cazul nostru, dimensiunea paginii va fi de 50). De obicei, ar trebui să alegeți o dimensiune care să fie mai mare decât numărul maxim pe care îl puteți afișa simultan utilizatorului. În cele din urmă, sunăm construi() să ne construim și să ne întoarcem a LiveData

7. Crearea Adaptorului PagedList

Pentru a ne arăta PagedList date într-un RecyclerView, avem nevoie de PagedListAdapter. Iată o definiție clară a acestei clase din documentele oficiale:

RecyclerView.Adapter clasa de bază pentru prezentarea datelor paginate de la PagedLists într-o RecyclerView.

Așa că noi creăm a PersonAdapter care se extinde PagedListAdapter.

importați android.arch.paging.PagedListAdapter importați android.content.Context importați android.support.v7.widget.RecyclerView importați android.view.LayoutInflater import android.view.Video import import android.view.ViewGroup import 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 ()) suprascrie distracția onBindViewHolder (holderPerson: PersonViewHolder, position: Int) var person = getItem (poziție) if (person == null) holderPerson.clear () else holderPerson.bind (person) suprascrie distracția onCreateViewHolder (parentă: ViewGroup, viewType: Int): PersonViewHolder retur PersonViewHolder (LayoutInflater.from (context) .inflate (R.layout.item_person, parent, false) class PersonViewHolder (vizualizare: View): RecyclerView.ViewHolder (vizualizare) var tvName: TextView = nume de viziune funny (persoană: Persoană) tvName.text = person.name fun clear () tvName.text = null

PagedListAdapter este folosit la fel ca orice altă subclasă de RecyclerView.Adapter. Cu alte cuvinte, trebuie să implementați metodele onCreateViewHolder () și onBindViewHolder ()

Pentru a extinde PagedListAdapter clasa abstractă, va trebui să furnizați - în constructorul său - tipul de PageLists (aceasta ar trebui să fie o clasă veche clasică Java: un POJO) și, de asemenea, o clasă care extinde ViewHolder care vor fi utilizate de adaptor. În cazul nostru, l-am dat Persoană și PersonViewHolder ca prim și, respectiv, al doilea argument. 

Rețineți că PagedListAdapter cere să-i dai o a DiffUtil.ItemCallback la PageListAdapter constructor. DiffUtil este a RecyclerView clasa de utilități care poate calcula diferența dintre două liste și poate afișa o listă de operații de actualizare care convertește prima listă în cea de-a doua. ItemCallback este o clasă statică abstractă interioară (interiorul DiffUtil) utilizat pentru a calcula diferența dintre două elemente non-null dintr-o listă. 

Mai exact, furnizăm PersonDiffCallback pentru noi PagedListAdapter constructor. 

importand android.support.v7.util.DiffUtil import com.chikeandroid.pagingtutsplus.data.Person clasa PersonDiffCallback: DiffUtil.ItemCallback(oldItem: Person, newItem: Person?): boolean return oldItem.id == newItem.id suprascrie distracția areContentsTheSame (oldItem: Person ?, newItem: Person?): 

Pentru că punem în aplicare DiffUtil.ItemCallback, trebuie să implementăm două metode: areItemsTheSame () și areContentsTheSame ()

  • areItemsTheSame este chemat să verifice dacă două obiecte reprezintă același element. De exemplu, dacă elementele dvs. au ID-uri unice, această metodă ar trebui să verifice egalitatea de identitate a acestora. Această metodă revine Adevărat dacă cele două elemente reprezintă același obiect sau fals dacă sunt diferite.
  • areContentsTheSame este chemat să verifice dacă două elemente au aceleași date. Această metodă revine Adevărat dacă conținutul articolelor este același sau fals dacă sunt diferite.

Al nostru PersonViewHolder clasa interioară este doar un tipic RecyclerView.ViewHolder. Este responsabil pentru legarea datelor, după cum este necesar din modelul nostru, în widget-urile pentru un rând din lista noastră. 

clasa PersonAdapter (contextul val: Context): PagedListAdapter(PersonDiewCallback ()) // ... clasa PersonViewHolder (vizualizare: Vizualizare): RecyclerView.ViewHolder (vizualizare) var tvName: TextView = nume de vizionare fun bind (person: Person) tvName.text = person.name () tvName.text = null

8. Afișarea rezultatului

În a noastră onCreate () a noastră Activitate principala, am făcut pur și simplu următoarele:

  • inițializa nostru viewModel utilizând clasa de utilitate ViewModelProviders
  • creați un exemplu de PersonAdapter
  • configurați nostru RecyclerView
  • obligați PersonAdapter la RecyclerView
  • observați LiveData și trimiteți PagedList obiecte de la PersonAdapter invocând submitList ()
importați android.arch.lifecycle.Observer import android.arch.lifecycle.ViewModelProviders importați 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 () privat lateinit var viewModel: PersonsViewModel suprascrie distracție onCreate (savedInstanceState: Bundle?) Super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) viewModel = ViewModelProviders.of (acest) .get (PersonsViewModel :: class.java) val adapter = PersonAdapter (acest) findViewById(Nume_aplicator_adaptator) adaptor subscribeUi adaptor privat subscribeUi (adaptor: PersonAdapter) viewModel.getPersonLiveData () observa (acest lucru, Observer names -> if (names! = Null) adapter.submitList (nume))

În cele din urmă, când rulați aplicația, iată rezultatul:

În timp ce derulați, camera este capabilă să prevină decalajele prin încărcarea a 50 de articole la un moment dat și a le pune la dispoziția noastră PersonAdapter, care este o subclasă de PagingListAdapter. Dar rețineți că nu toate sursele de date vor fi încărcate rapid. Viteza de încărcare depinde, de asemenea, de puterea de procesare a dispozitivului Android. 

9. Integrarea cu RxJava

Dacă utilizați sau doriți să utilizați RxJava în proiectul dvs., biblioteca de paginare include un alt artefact util: RxPagedListBuilder. Folosești acest artefact în loc de LivePagedListBuilder pentru suportul RxJava. 

Pur și simplu creați o instanță de RxPagedListBuilder, furnizând aceleași argumente ca și dumneavoastră LivePagedListBuilder- DataSource.Factory și mărimea paginii. Apoi, sunați buildObservable () sau buildFlowable () pentru a reveni Observabil sau curgător pentru tine PagedList respectiv. 

Să furnizeze explicit Scheduler pentru operația de încărcare a datelor, apelați metoda de setare setFetchScheduler (). Pentru a oferi, de asemenea, Scheduler pentru livrarea rezultatului (de ex. AndroidSchedulers.mainThread ()), pur și simplu sunați setNotifyScheduler (). În mod implicit, setNotifyScheduler () implicit la firul UI, în timp ce setFetchScheduler () implicit la piscina de file I / O. 

Concluzie

În acest tutorial, ați învățat cum să utilizați cu ușurință componenta Paging din Componentele Android Architecture (care fac parte din Android Jetpack) împreună cu Room. Acest lucru ne ajută să încărcăm în mod eficient seturi mari de date din baza de date locală pentru a permite o experiență mai ușoară a utilizatorului în timp ce derulați printr-o listă în RecyclerView

Vă recomandăm să verificați documentația oficială pentru a afla mai multe despre biblioteca Paging din Android.

Cod