Am acoperit deja o mulțime de terenuri în seria noastră de componente Android Architecture Components. Am început să vorbim despre ideea din spatele noii arhitecturi și despre componentele cheie prezentate la Google I / O. În cel de-al doilea post, am început explorarea profundă a componentelor principale ale pachetului, urmărind cu atenție Ciclu de viață
și LiveModel
componente. În acest post, vom continua să explorăm Componentele de Arhitectură, de această dată analizând minunatul LiveData
component.
Presupun că sunteți familiarizat cu conceptele și componentele acoperite în ultimele tutoriale, cum ar fi Ciclu de viață
, LifecycleOwner
, și LifecycleObserver
. Dacă nu sunteți, aruncați o privire la primul post din această serie, unde discutăm ideea generală din spatele noii arhitecturi Android și a componentelor sale.
Vom continua să construim pe exemplul aplicației pe care am început-o în ultima parte a acestei serii. O puteți găsi în reparația GitHub tutorial.
LiveData
este un deținător de date. Este capabil să fie observat, poate deține orice fel de date și, în plus, este, de asemenea ciclului de viață-conștient. În termeni practici, LiveData
poate fi configurat să trimită actualizări de date doar atunci când observatorul său este activ. Datorită conștientizării pe durata ciclului de viață, atunci când este observată de către a LifecycleOwner
LiveData
componenta va trimite actualizări numai atunci când este observatorul Ciclu de viață
este încă activă și va elimina relația observată odată cu observarea observatorului Ciclu de viață
este distrusă.
LiveData
componenta are multe caracteristici interesante:
Ciclu de viață
LiveData
A LiveData
Componenta trimite actualizări de date numai atunci când observatorul său este "activ". Când se observă de către a LifecycleOwner
, LiveData
componenta considera observatorul ca fiind activ numai in timpul lui Ciclu de viață
este pe state ÎNCEPUT
sau reluate
, altfel va considera observatorul ca inactiv. În timpul stadiului inactiv al observatorului, LiveData
va opri fluxul de actualizare a datelor, până când observatorul său va deveni activ din nou. Dacă observatorul este distrus, LiveData
va elimina referința la observator.
Pentru a realiza acest comportament, LiveData
creează o legătură strânsă cu observatorul Ciclu de viață
când este observat de către a LifecycleOwner
. Această capacitate facilitează evitarea scurgerilor de memorie la observare LiveData
. Cu toate acestea, dacă procesul de observare este chemat fără a LifecycleOwner
, LiveData
componenta nu va reacționa la Ciclu de viață
stare, iar statutul observatorului trebuie manevrat manual.
Pentru a observa a LiveData
, apel observa (LifecycleOwner, Observer
sau observeForever (Observer
.
observa (LifecycleOwner, Observer)
: Acesta este modul standard de observare a LiveData
. Legătura dintre observator și a Ciclu de viață
, schimbare LiveData
- starea activă și inactivă în conformitate cu LifecycleOwner
starea curenta.observeForever (Observer)
: Această metodă nu utilizează a LifecycleOwner
, asa ca LiveData
nu va putea răspunde Ciclu de viață
evenimente. Atunci când utilizați această metodă, este foarte important să apelați removeObserver (Observer)
, în caz contrar, observatorul nu poate fi colectat de gunoi, provocând o scurgere de memorie.// sunat de la o locație LifecycleOwner.observe (// LifecycleOwner this, // creând un observer Observer location -> info ("locație: $ location !!. latitude, $ location.longitude") // Observare fără LifecycleOwner val observer = Observer locație -> info ("locație: $ location !!. Latitude, $ location.longitude")) location.observeForever (observator) // when observer without a LivecyleOwner // este necesar să eliminați observatorii la un loc point.removeObserver (observator)
LiveData
Tipul generic din LiveData
clasa definește tipul de date care vor fi păstrate. De exemplu, LiveData
deține Locație
date. Sau LiveData
deține a Şir
.
Există două metode principale care trebuie luate în considerare în implementarea componentei: onActive ()
și onInactive ()
. Ambele metode reacționează la starea observatorului.
În proba noastră de proiect am folosit o mulțime de LiveData
obiecte, dar am implementat doar unul: LocationLiveData
. Clasa se ocupă de GPS Locație
, depășind poziția actuală numai pentru un observator activ. Observați că clasa își actualizează valoarea pe onLocationChanged
metodă, trecând la observatorul activ actual actualizat Locație
date.
clasa LocationLiveData @Inject constructor (context: Context): LiveData(), LocListener, AnkoLogger private val locationManager: LocationManager = context.getSystemService (Context.LOCATION_SERVICE) ca LocationManager @SuppressLint ("MissingPermission") suprascrie distracție onInactive () locationManager.removeUpdates (this) SuppressLint ("MissingPermission") distracție refreshLocation () info ("refreshLocation") locationManager.requestSingleUpdate (LocationManager.GPS_PROVIDER, this, null)
MutableLiveData
Clasa de ajutor MutableLiveData
este o clasă de ajutor care se extinde LiveData
, și expune postValue
și SetValue
metode. În afară de asta, se comportă exact ca părintele său. Pentru ao utiliza, definiți tipul de date pe care le deține, cum ar fi MutableLiveData
să dețină o Şir
, și să creați o instanță nouă.
val myData: MutableLiveData= MutableLiveData ()
Pentru a trimite actualizări unui observator, apelați postValue
sau SetValue
. Comportamentul acestor metode este destul de similar; in orice caz, SetValue
va seta direct o nouă valoare și poate fi apelat numai din firul principal, în timp ce postValue
creează o nouă sarcină pe firul principal pentru a seta noua valoare și poate fi apelată dintr-un fir de fundal.
distracție updateData () // trebuie să fie apelat din firul principal myData.value = api.getUpdate distracție updateDataFromBG () // poate fi sunat din bg thread myData.postValue (api.getUpdate)
Este important să se considere că, din moment ce postValue
metoda creează un nou Sarcină
și mesaje pe firul principal, acesta va fi mai lent decât apelurile directe către SetValue
.
LiveData
În cele din urmă, va trebui să schimbați a LiveData
și să-i difuzeze noua valoare observatorului său. Sau poate că trebuie să creați o reacție în lanț între două LiveData
obiecte, făcând una să reacționeze la schimbările de pe altul. Pentru a face față ambelor situații, puteți utiliza funcția transformări
clasă.
Transformations.map
aplică o funcție pe LiveData
instanța și trimite rezultatul către observatorii săi, oferindu-vă posibilitatea de a manipula valoarea datelor.
Este foarte ușor de implementat Transformations.map
. Tot ce trebuie să faceți este să oferiți o LiveData
care trebuie respectate și a Funcţie
pentru a fi numit atunci când observat LiveData
modificări, amintiți - vă că Funcţie
trebuie să returneze noua valoare a modelului transformat LiveData
.
Să presupunem că aveți un a LiveData
care trebuie să sune un API când a Şir
valoare, ca un câmp de căutare, se modifică.
// LiveData care apelează api // când 'searchLive' își schimbă valoarea apiLive: LiveData= Transformations.map (căutareLive, interogare -> return @ map api.call (interogare)) // De fiecare dată când 'searchLive' are // valoarea lui actualizată, va apela // 'apiLive' Transformation.map fun update (interogare: String) searchLive.postValue (interogare)
Transformations.switchMap
este destul de similar cu Transformations.map
, dar trebuie să returneze a LiveData
obiect ca rezultat. Este ceva mai greu de folosit, dar vă permite să construiți reacții puternice în lanț.
În proiectul nostru, am folosit Transformations.switchMap
pentru a crea o reacție între LocationLiveData
și ApiResponse
.
Transformation.switchMap
observă LocationLiveData
schimbări.LocationLiveData
valoarea actualizată este utilizată pentru a apela MainRepository
pentru a obține vremea pentru locația specificată.OpenWeatherService
care produce o LiveData>
ca rezultat.LiveData
este observată de către a MediatorLiveData
, care este responsabil pentru modificarea valorii primite și actualizarea vremii expuse în stratul de vizualizare.class MainViewModel @ Constructor injectare (depozit valore privat: MainRepository): ViewModel (), AnkoLogger // Locația privată val: LocLiveData = repository.locationLiveDa () private var weatherByLocationResponse: LiveData> = Transformations.switchMap (locație, l -> info ("locationByLocation: \ nlocație: $ l") return @ switchMap repository.getWeatherByLocation (l)
Feriți-vă de operațiunile consumatoare de timp în dvs. LiveData
transformări, totuși. În codul de mai sus, ambele transformări
metodele rulează pe firul principal.
MediatorLiveData
MediatorLiveData
este un tip mai avansat de LiveData
. Are capabilități foarte asemănătoare cu cele ale transformări
clasa: este capabil să reacționeze la altele LiveData
obiecte, apelarea a Funcţie
când datele observate se modifică. Cu toate acestea, are multe avantaje în comparație cu transformări
, deoarece nu are nevoie să ruleze pe firul principal și poate observa mai multe LiveData
o dată.
Pentru a observa a LiveData
, apel addSource (LiveData
, ceea ce face ca observatorul să reacționeze la , Observator)onChanged
din metoda dată LiveData
. Pentru a opri observarea, sunați removeSource (LiveData
.)
val mediatorData: MediatorLiveData= MediatorLiveData () mediatorData.addSource (dataA, value -> // reacționează la valoarea valorii ("valoarea mea valoarea $")) mediatorData.addSource (dataB, value - $ value ") // putem elimina sursa odată utilizată mediatorData.removeSource (dataB))
În proiectul nostru, datele observate de stratul de vizualizare care conține vremea de expunere sunt a MediatorLiveData
. Componenta observă alte două LiveData
obiecte: weatherByLocationResponse
, care primește actualizări meteorologice după locație, și weatherByCityResponse
, care primește actualizări meteorologice după numele orașului. De fiecare dată când aceste obiecte sunt actualizate, weatherByCityResponse
va actualiza stratul de vizualizare cu vremea cerută.
În MainViewModel
, observăm LiveData
și să furnizeze vreme
obiect de vedere.
class MainViewModel @ Inject constructor (depozit privat val: MainRepository): ViewModel (), AnkoLogger // ... // Valoarea observată de View. // Transformă WeatherResponse la WeatherMain. vreme privată de vale: MediatorLiveData> = MediatorLiveData () // recuperarea vremii LiveData distracție getWeather (): LiveData > info ("getWeather") întoarce vremea distracție privată addWeatherSources () info ("addWeatherSources") weather.addSource (weatherByCityResponse, w -> info ("addWeatherSources: \ nweather: $ w !! ) updateWeather (w.data !!)) weather.addSource (WeatherByLocationResponse, w -> info ("addWeatherSources: weatherByLocationResponse: \ n $ w !! !) distracție privată updateWeather (w: WeatherResponse) info ("updateWeather") // obținerea vremii de astăzi val weatherMain = WeatherMain.factory (w) // salva pe preferințele partajate repository.saveWeatherMainOnPrefs (weatherMain) valoare meteo weather.postValue (ApiResponse (data = weatherMain)) init // ... addWeatherSources ()
În Activitate principala
, vremea este observată și rezultatul său este arătat utilizatorului.
private fun initModel () // Obțineți ViewModel viewModel = ViewModelProviders.of (this, viewModelFactory) .get (MainViewModel :: class.java) dacă (viewModel! = null) // observați modul meteo viewModel !!. getWeather (). observați (această @ MainActivity, Observer r -> if (r! = null) informația ("Weather received on MainActivity: \ n $ r") if (! r.hasError eroare info ("vreme: $ r.data") dacă (r.data! = null) setUI (r.data) fals) dacă (r.error !. statusCode! = 0) if (r.error !, message! = null) toast (r.error.message !!) altceva toast ("A apărut o eroare") )
MediatorLiveData
a fost de asemenea utilizat ca bază a unui obiect care gestionează apelurile către API-ul OpenWeatherMap. Aruncați o privire la această implementare; este mai avansată decât cele de mai sus și merită studiat. Dacă sunteți interesat, aruncați o privire la OpenWeatherService
, acordând o atenție deosebită Mediator
clasă.
Suntem aproape la sfârșitul explorării componentelor de arhitectură Android. Până acum, ar trebui să înțelegeți suficient pentru a crea unele aplicații puternice. În următorul post, vom explora Cameră
, un ORM care se împachetează SQLite
și poate produce LiveData
rezultate. Cameră
componentă se potrivește perfect în această arhitectură și este ultima piesă a puzzle-ului.
Ne vedem în curând! Între timp, verificați câteva dintre celelalte postări ale noastre privind dezvoltarea aplicațiilor Android!