In deze zelfstudie laat ik je zien hoe je de Paging-bibliotheek van de Android-architectuurcomponenten gebruikt met een Room-backed-database in een Android-app.
U leert hoe u de Paging-bibliotheek gebruikt om grote gegevenssets efficiënt te laden vanuit een Room-backed-database, waardoor uw gebruikers een soepelere ervaring krijgen tijdens het scrollen in een RecyclerView.
Als je deze zelfstudie wilt kunnen volgen, heb je het volgende nodig:
Actuele gegevens
en Room database)Als je de architectuurcomponenten nog niet hebt geleerd, raden we je ten zeerste aan om onze geweldige serie te bekijken, alles over Android Architecture Components van Tin Megali. Zorg ervoor dat je erin gaat duiken!
Een voorbeeldproject voor deze zelfstudie is te vinden op onze GitHub-repo, zodat u deze eenvoudig kunt volgen.
De paging-bibliotheek is een andere bibliotheek die is toegevoegd aan de architectuurcomponenten. De bibliotheek helpt bij het efficiënt beheren van het laden en weergeven van een grote gegevensset in de RecyclerView
. Volgens de officiële documenten:
Met de pagingbibliotheek kunt u gemakkelijker gegevens geleidelijk en sierlijk in uw app ladenRecyclerView
.
Als een deel van uw Android-app een grote gegevensset van een lokale of externe gegevensbron gaat weergeven maar slechts een deel ervan tegelijkertijd weergeeft, kunt u overwegen de Paging-bibliotheek te gebruiken. Dit zal de prestaties van uw app helpen verbeteren!
Nu u een inleiding tot de paging-bibliotheek hebt gezien, kunt u zich afvragen waarom u deze gebruikt? Hier zijn enkele redenen waarom u zou moeten overwegen om het te gebruiken bij het laden van grote gegevenssets in a RecyclerView
.
Het zal niet efficiënt zijn bij het werken met een grote hoeveelheid gegevens, omdat de onderliggende gegevensbron alle gegevens ophaalt, hoewel slechts een deelverzameling van die gegevens aan de gebruiker zal worden getoond. In zo'n situatie zouden we moeten overwegen om de gegevens te pagen.
Start je Android Studio 3 op en maak een nieuw project met een lege activiteit genaamd Hoofdactiviteit
. Zorg ervoor om te controleren Inclusief Kotlin-ondersteuning.
Nadat u een nieuw project hebt gemaakt, voegt u de volgende afhankelijkheden toe in uw build.gradle. In deze tutorial gebruiken we de nieuwste Paging-bibliotheekversie 1.0.1, terwijl Room 1.1.1 is (vanaf dit schrijven).
afhankelijkheden implementatie fileTree (dir: 'libs', include: ['* .jar']) implementatie "android.arch.persistence.room:runtime:1.1.1" kapt "android.arch.persistence.room:compiler:1.1 .1 "implementatie" android.arch.paging: runtime: 1.0.1 "implementatie" com.android.support:recyclerview-v7:27.1.1 "
Deze artefacten zijn beschikbaar in de Maven-repository van Google.
allprojects repositories google () jcenter ()
Door de afhankelijkheden toe te voegen, hebben we Gradle geleerd hoe de bibliotheek te vinden. Vergeet niet om uw project te synchroniseren nadat u ze hebt toegevoegd.
Maak een nieuwe Kotlin-gegevensklasse Persoon
. Voor de eenvoud, onze Persoon
entiteit heeft slechts twee velden:
ID kaart
)naam
)Voeg daarnaast een toString (
methode die eenvoudig de naam
.
import android.arch.persistence.room.Entity import android.arch.persistence.room.PrimaryKey @Entity (tableName = "persons") dataklasse Person (@PrimaryKey val id: String, val name: String) override fun toString ( ) = naam
Zoals u weet, hebben we toegang tot data-objecten (DAO's) nodig om toegang te krijgen tot de gegevens van onze app met de Room-bibliotheek. In ons eigen geval hebben we een 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 @Dao interface PersonDao @Query ("SELECT * FROM persons") fun getAll (): LiveData> @Query ("SELECT * FROM persons") fun getAllPaged (): DataSource.Factory
@Gebruik leuk invoegenAlle (personen: lijst ) @Wissen grappig verwijderen (persoon: persoon)
In onze PersonDao
klas, we hebben er twee @Query
methoden. Een van hen is alles krijgen()
, welke een a retourneert Actuele gegevens
die een lijst bevat van Persoon
voorwerpen. De andere is getAllPaged ()
, welke een a retourneert DataSource.Factory
.
Volgens de officiële documenten, de Databron
les is de:
Basisklasse voor het laden van pagina's met snapshotgegevens in een PagedList
.
EEN PagedList
is een speciaal soort Lijst
voor het weergeven van pagina-gegevens in Android:
EENPagedList
is een lijst die zijn gegevens laadt in chunks (pagina's) van aDatabron
. Items kunnen worden geopend mette krijgen (int)
, en verder laden kan worden geactiveerd metloadAround (int)
.
We hebben het Fabriek
statische methode in de Databron
class, die dienst doet als een fabriek (objecten maken zonder de exacte klasse van het object te hoeven opgeven dat wordt gemaakt) voor de Databron
. Deze statische methode omvat twee gegevenstypen:
Databron
. Houd er rekening mee dat voor een Room-query de pagina's zijn genummerd, dus we gebruiken Geheel getal
als het type pagina-ID. Het is mogelijk om "ingetoetste" pagina's te gebruiken met behulp van de paging-bibliotheek, maar Room biedt dat momenteel niet. Databron
s.Dit is wat onze database met Room-klassen is AppDatabase
lijkt op:
importeer 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) abstract class AppDatabase: RoomDatabase () abstract fun personDao (): PersonDao-begeleidende object // voor Singleton instantiation @Volatile private var instance: AppDatabase? = null fun getInstance (context: Context): AppDatabase return instance?: synchrone (this) instance?: buildDatabase (context) .also instance = it private fun buildDatabase (context: Context): AppDatabase return Room .databaseBuilder (context, AppDatabase :: class.java, DATABASE_NAME) .addCallback (object: RoomDatabase.Callback () override fun onCreate (db: SupportSQLiteDatabase) super.onCreate (db) val request = OneTimeWorkRequestBuilder() .build () WorkManager.getInstance () ?. wacht (aanvraag)) .build ()
Hier hebben we één exemplaar van onze database gemaakt en vooraf ingevuld met gegevens met de nieuwe WorkManager-API. Merk op dat de vooraf ingevulde gegevens slechts een lijst van 1000 namen zijn (neem een kijkje in de verstrekte voorbeeldbroncode voor meer informatie).
Voor onze gebruikersinterface om gegevens op een levenscyclusbewuste manier op te slaan, te observeren en te bedienen, hebben we een ViewModel
. Onze PersonsViewModel
, waardoor de AndroidViewModel
klas, gaat functioneren als onze 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 PersonsViewModel constructor (applicatie: Applicatie): AndroidViewModel (applicatie) private var personsLiveData: LiveData> init val factory: DataSource.Factory = AppDatabase.getInstance (getApplication ()). PersonDao (). GetAllPaged () val pagedListBuilder: LivePagedListBuilder = LivePagedListBuilder (fabriek, 50) personsLiveData = pagedListBuilder.build () fun getPersonsLiveData () = personsLiveData
In deze klasse hebben we een enkel veld met de naam personsLiveData
. Dit veld is gewoon een Actuele gegevens
dat bevat een PagedList
van Persoon
voorwerpen. Omdat dit een is Actuele gegevens
, onze gebruikersinterface (de Activiteit
of Fragment
) gaat deze gegevens observeren door de getter-methode aan te roepen getPersonsLiveData ()
.
We zijn geïnitialiseerd personsLiveData
binnen in de in het
blok. In dit blok krijgen we de DataSource.Factory
door het te bellen AppDatabase
singleton voor de PersonDao
voorwerp. Wanneer we dit object krijgen, bellen we getAllPaged ()
.
We maken vervolgens een LivePagedListBuilder
. Dit is wat de officiële documentatie zegt over een LivePagedListBuilder
:
Bouwer voorActuele gegevens
, gegeven eenDataSource.Factory
en eenPagedList.Config
.
Wij leveren zijn constructeur een DataSource.Factory
als het eerste argument en de paginagrootte als het tweede argument (in ons eigen geval zal het paginaformaat 50 zijn). Meestal moet u een grootte kiezen die hoger is dan het maximumaantal dat u in één keer aan de gebruiker kunt weergeven. Uiteindelijk bellen we bouwen()
om te bouwen en naar ons terug te keren Actuele gegevens
.
Om onze te laten zien PagedList
gegevens in a RecyclerView
, we hebben een ... nodig PagedListAdapter
. Hier is een duidelijke definitie van deze klasse uit de officiële documenten:
RecyclerView.Adapter
basisklasse voor het presenteren van wisselbare gegevens vanPagedList
s in aRecyclerView
.
Dus we creëren een PersonAdapter
dat breidt zich uit 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 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 (position) if (person == null) holderPerson.clear () else holderPerson.bind (person) override fun onCreateViewHolder (boven: ViewGroup, viewType: Int): PersonViewHolder return PersonViewHolder (LayoutInflater.from (context) .inflate (R.layout.item_person, parent, false)) 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
PagedListAdapter
wordt net als elke andere subklasse van gebruikt RecyclerView.Adapter
. Met andere woorden, u moet de methoden implementeren onCreateViewHolder ()
en onBindViewHolder ()
.
Om de PagedListAdapter
abstracte klasse, moet je in zijn constructor het type PageLists
(dit zou een gewone oude Java-klasse moeten zijn: een POJO) en ook een klasse die de ViewHolder
dat wordt gebruikt door de adapter. In ons geval hebben we het gegeven Persoon
en PersonViewHolder
als respectievelijk het eerste en tweede argument.
Let daar op PagedListAdapter
vereist dat je het doorgeeft a DiffUtil.ItemCallback
naar de PageListAdapter
bouwer. DiffUtil
is een RecyclerView
utility-klasse die het verschil tussen twee lijsten kan berekenen en een lijst met updatebewerkingen uitvoert die de eerste lijst in de tweede omzet. ItemCallback
is een innerlijke abstracte statische klasse (binnen DiffUtil
) gebruikt voor het berekenen van het verschil tussen twee niet-lege items in een lijst.
Concreet leveren wij PersonDiffCallback
naar onze PagedListAdapter
bouwer.
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
Omdat we aan het implementeren zijn DiffUtil.ItemCallback
, we moeten twee methoden implementeren: areItemsTheSame ()
en areContentsTheSame ()
.
areItemsTheSame
wordt geroepen om te controleren of twee objecten hetzelfde item vertegenwoordigen. Als uw items bijvoorbeeld unieke id's hebben, moet deze methode de gelijkheid van id's controleren. Deze methode retourneert waar
als de twee items hetzelfde object of vertegenwoordigen vals
als ze anders zijn.areContentsTheSame
wordt geroepen om te controleren of twee items dezelfde gegevens hebben. Deze methode retourneert waar
als de inhoud van de items hetzelfde zijn of vals
als ze anders zijn.Onze PersonViewHolder
innerlijke klasse is slechts een typisch voorbeeld RecyclerView.ViewHolder
. Het is verantwoordelijk voor het binden van gegevens zoals nodig uit ons model in de widgets voor een rij in onze lijst.
class PersonAdapter (val context: Context): PagedListAdapter(PersonDiffCallback ()) // ... class PersonViewHolder (weergave: Weergave): RecyclerView.ViewHolder (weergave) var tvName: TextView = view.name fun bind (persoon: persoon) tvName.text = person.name fun clear () tvName.text = null
In onze onCreate ()
van onze Hoofdactiviteit
, we hebben gewoon het volgende gedaan:
ViewModel
veld met behulp van de utility-klasse ViewModelProviders
PersonAdapter
RecyclerView
PersonAdapter
naar de RecyclerView
Actuele gegevens
en stuur het PagedList
objecten naar de PersonAdapter
door aan te roepen 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: PersonsViewModel override 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 (adapter) private fun subscribeUi (adapter: PersonAdapter) viewModel.getPersonLiveData (). Observe (this, Observer names -> if (names! = Null) adapter.submitList (namen))
Eindelijk, wanneer u de app uitvoert, is hier het resultaat:
Tijdens het scrollen kan Room leemtes voorkomen door 50 items tegelijk te laden en beschikbaar te maken voor onze PersonAdapter
, welke een subklasse is van PagingListAdapter
. Houd er echter rekening mee dat niet alle gegevensbronnen snel worden geladen. De laadsnelheid is ook afhankelijk van de verwerkingskracht van het Android-apparaat.
Als u RxJava in uw project gebruikt of wilt gebruiken, bevat de paging-bibliotheek een ander nuttig artefact: RxPagedListBuilder
. U gebruikt dit artefact in plaats van LivePagedListBuilder
voor RxJava-ondersteuning.
U maakt eenvoudig een instantie van RxPagedListBuilder
, het leveren van dezelfde argumenten als voor LivePagedListBuilder
-de DataSource.Factory
en het paginaformaat. Jij belt dan buildObservable ()
of buildFlowable ()
terugkeren waarneembaar
of flowable
Voor jouw PagedList
respectievelijk.
Om expliciet de Scheduler
voor het laden van gegevens, noem je de methode setter setFetchScheduler ()
. Om ook de Scheduler
voor het afleveren van het resultaat (bijv. AndroidSchedulers.mainThread ()
), gewoon bellen setNotifyScheduler ()
. Standaard, setNotifyScheduler ()
standaard naar de UI-thread, terwijl setFetchScheduler ()
standaard de I / O thread pool.
In deze zelfstudie leer je hoe je de Paging-component van de Android Architecture Components (die onderdeel zijn van Android Jetpack) met Room kunt gebruiken. Dit helpt ons bij het efficiënt laden van grote gegevenssets uit de lokale database om een soepelere gebruikerservaring mogelijk te maken tijdens het scrollen door een lijst in de RecyclerView
.
Ik raad ten zeerste aan om de officiële documentatie te bekijken voor meer informatie over de paging-bibliotheek in Android.