RxJava este una dintre cele mai populare biblioteci pentru aducerea programării reactive pe platforma Android, iar în această serie de trei părți vă oferim cum să începeți să utilizați această bibliotecă în propriile dvs. proiecte Android.
Pentru a începe cu RxJava 2 pentru Android, ne-am uitat la ce este RxJava și ce a oferit dezvoltatorilor Android, înainte de a crea o aplicație Hello World care a demonstrat cele trei componente principale ale RxJava: Observabil
, un Observator
, și un abonament.
În operatorii de programare reactivă din tutorialul RxJava 2, am analizat modul în care puteți efectua transformări complexe de date utilizând operatorii și cum puteți combina Operator
și Scheduler
s pentru a face în cele din urmă multithreading pe Android o experiență fără durere.
Am atins, de asemenea, pe RxAndroid, o bibliotecă special concepută pentru a vă ajuta să utilizați RxJava în proiectele Android, dar mai sunt multe de explorat în RxAndroid. Deci, în acest post, mă voi concentra doar pe familia de biblioteci RxAndroid.
La fel ca RxJava, RxAndroid a suferit o revizuire masivă în lansarea versiunii 2. Echipa RxAndroid a decis să modularizeze biblioteca, deplasând o mare parte din funcționalitatea sa în module dedicate RxAndroid.
În acest articol, vă voi arăta cum să configurați și să utilizați unele dintre cele mai populare și puternice module RxAndroid - inclusiv o bibliotecă care poate face ascultători, TextWatchers
un lucru din trecut, oferindu-vă capacitatea de a trata orice Eveniment UI Android ca un Observabil
.
Și din moment ce scurgerile de memorie cauzate de abonamentele incomplete reprezintă cel mai mare dezavantaj în utilizarea aplicației RxJava în aplicațiile Android, vă voi arăta, de asemenea, cum să utilizați un modul RxAndroid care poate gestiona procesul de abonare pentru dvs. Până la sfârșitul acestui articol, veți ști cum să utilizați RxJava în orice Activitate
sau Fragment
, fără riscul întâlnirii orice RxJava legate de scurgeri de memorie.
Reacționarea la evenimentele UI, cum ar fi robinete, rafturi și introducerea textului, reprezintă o parte fundamentală a dezvoltării aproape oricăror aplicații Android, însă gestionarea evenimentelor cu UI Android nu este deosebit de simplă.
În mod obișnuit, veți reacționa la evenimentele din UI utilizând o combinație de ascultători, manipulanți, TextWatchers
, și, eventual, alte componente în funcție de tipul de interfață utilizată pe care o creați. Fiecare dintre aceste componente vă cere să scrieți o cantitate semnificativă de cod de boilerplate și să faceți lucrurile mai rău, nu există nici o coerență în modul în care implementați aceste componente diferite. De exemplu, te descurci OnClick
evenimente prin implementarea unui program OnClickListener
:
Buton buton = (buton) findViewById (R.id.button); butonul.setOnClickListener (noul View.OnClickListener () @Override public void onClick (vizualizați v) // Efectuați unele lucrări //);
Dar acest lucru este complet diferit de modul în care ați implementa un TextWatcher:
final EditText nume = (EditText) v.findViewById (R.id.name); // Crearea unui TextWatcher și specificarea faptului că acest TextWatcher ar trebui să fie apelat ori de câte ori modificările conținutului EditText se modifică // name.addTextChangedListener (nou TextWatcher () @Override public void beforeTextChanged (CharSequence, int start, int count, int after) @ Înlocuiți void public onTextChanged (CharSequence s, int start, int înainte, int count) // Efectuați niște lucrări // @Override public void afterTextChanged (Editable s) );
Această lipsă de consecvență poate adăuga o mare complexitate codului dvs. Și dacă aveți componente UI care depind de ieșirea altor componente UI, atunci pregătiți-vă pentru ca lucrurile să devină și mai complicate! Chiar și un caz simplu de utilizare - cum ar fi solicitarea utilizatorului de a-și scrie numele într-un Editează textul
astfel încât să puteți personaliza textul care apare ulterior TextViews
-cere apelări imbricate, care sunt dificil de implementat și menținut. (Unii oameni se referă la apelurile imbricate ca la "callback iad").
În mod evident, o abordare standardizată a manipulării evenimentelor UI are potențialul de a vă simplifica foarte mult codul și RxBinding este o bibliotecă care intenționează să facă acest lucru, oferind legături care vă permit să convertiți orice Android Vedere
eveniment într-un Observabil
.
Odată ce ați convertit un eveniment de vizualizare într-un Observabil
, va emite evenimentele UI ca fluxuri de date pe care le puteți abona în exact același mod în care vă veți abona la orice alt Observabil
.
Din moment ce am văzut deja cum ați capta un eveniment de clic utilizând standardul Android OnClickListener
, să vedem cum ați obține aceleași rezultate utilizând RxBinding:
import com.jakewharton.rxbinding.view.RxView; ... buton buton = (buton) findViewById (R.id.button); RxView.clicks (buton) .subscribe (aVoid -> // Efectuați niște lucrări aici //);
Nu numai că această abordare este mai concisă, dar este o implementare standard pe care o puteți aplica la toate evenimentele UI care apar în întreaga aplicație. De exemplu, capturarea textului de intrare urmează același model ca și captarea evenimentelor clic:
RxTextView.textChanges (editText) .subscribe (charSequence -> // Efectuați o lucrare aici //);
Deci, puteți vedea exact cum poate RxBinding să simplifice codul aplicației din aplicația dvs., să creăm o aplicație care demonstrează câteva dintre aceste legături în acțiune. De asemenea, voi include o Vedere
asta depinde de ieșirea altui Vedere
, pentru a demonstra modul în care RxBinding simplifică crearea relațiilor dintre componentele UI.
Această aplicație va consta în:
Buton
care afișează a Paine prajita
când a fost lovit.Editează textul
care detectează modificările textului.TextView
care actualizează pentru a afișa conținutul Editează textul
.Creați un proiect Android Studio cu setările dorite, apoi deschideți nivelul modulului build.gradle și adăugați ultima versiune a bibliotecii RxBinding ca dependență de proiect. În interesul păstrării la minimum a codului de boilerplate, voi folosi și lambda, așa că mi-am actualizat build.gradle fișier pentru a sprijini această caracteristică Java 8:
aplicați plugin-ul: 'com.android.application' șiroid compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig aplicațieId "com.jessicathornsby.myapplication" minSdkVersion 23 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner. AndroidJUnitRunner "// Activați toolkit Jack // jackOptions enabled true buildTypes release minifyEnabled false proguardFiles getDefaultProguardFile ('proguard-android.txt'), 'proguard-rules.pro' // Setați sursaCompatibilitate și targetCompatibility to JavaVersion.VERSION_1_8 // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 dependente compile fileTree (dir: 'libs', include: ['* .jar']) androidTestCompile ('com.android.support.test.espresso : espresso-core: 2.2.2 ', exclude grup:' com.android.support ', modul:' support-adnotations ') // Adăugați miezul bibliotecii RxBinding // compile' com.jakewharton.rxbinding: rxbinding: 0.4.0 compilați com .android.support: appcompat-v7: 25.3.0 '// Nu uitați să adăugați dependențele RxJava și RxAndroid // compilați' io.reactivex.rxjava2: rxandroid: 2.0.1 'compile' io.reactivex.rxjava2: rxjava: 2.0.5 'testCompile' junit: junit: 4.12 '
Când lucrați cu mai multe biblioteci RxJava, este posibil să întâlniți a Fișiere duplicate copiate în APK META-INF / DEPENDENCIES mesaj de eroare la momentul compilării. Dacă întâmpinați această eroare, soluția este de a suprima aceste fișiere duplicate prin adăugarea următoarelor la nivelul modulului build.gradle fişier:
android packagingOptions // Utilizați "exclude" pentru a indica la fișierul (sau fișierele) specific că Android Studio se plânge despre // excludeți "META-INF / rxjava.properties"
Sincronizați-vă fișierele Gradle și apoi creați un aspect format din a Buton
, un Editează textul
, și a TextView
:
Acum, să ne uităm la modul în care ați folosi aceste RxBinding pentru a capta diversele evenimente UI pe care cererea noastră trebuie să le reacționeze. Pentru început, declarați importurile și definiți Activitate principala
clasă.
pachet com.jessicathornsby.myapplication; import android.os.Bundle; import șiroid.support.v7.app.AppCompatActivity; import șiroid.widget.Button; importă android.widget.EditText; import șiroid.widget.TextView; importă android.widget.Toast; // Importul clasei view.RxView, astfel încât să puteți folosi RxView.clicks // import com.jakewharton.rxbinding.view.RxView; // Import widget.RxTextView pentru a putea folosi RxTextView.textChanges // import com.jakewharton.rxbinding.widget.RxTextView; clasa publica MainActivity extinde AppCompatActivity @Override protejat void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); Buton buton = (buton) findViewById (R.id.button); TextView textView = (TextView) findViewById (R.id.textView); EditareText editText = (EditText) findViewById (R.id.editText); // Codul pentru legături merge aici // // ... //
Acum puteți începe adăugarea legărilor pentru a răspunde evenimentelor UI. RxView.clicks
metoda este utilizată pentru a lega evenimentele clic. Creați o obligație pentru afișarea unui toast ori de câte ori faceți clic pe buton:
RxView.clicks (buton) .subscribe (aVoid -> Toast.makeText (MainActivity.this, "RxView.clicks", Toast.LENGTH_SHORT) .show (););
Apoi, utilizați RxTextView.textChanges ()
metoda de a reacționa la un eveniment de schimbare de text prin actualizarea TextView
cu conținutul nostru Editează textul
.
RxTextView.textChanges (editText) .subscribe (charSequence -> textView.setText (charSequence););
Când rulați aplicația dvs., veți sfârși cu un ecran ca mai jos.
Instalați proiectul pe un smartphone smartphone sau tabletă Android sau într-un AVD compatibil și apoi petreceți ceva timp interacționând cu diferitele elemente ale UI. Aplicația dvs. ar trebui să reacționeze la evenimentele pe clic și la introducerea textului ca în mod normal - și toate fără un ascultător, pentru funcția TextWatcher sau invers!
În timp ce biblioteca de bază RxBinding oferă legături pentru toate elementele UI care alcătuiesc platforma standard Android, există, de asemenea, module RxBinding frate care oferă legături pentru Vizualizările care sunt incluse în diverse biblioteci de suport ale Android.
Dacă ați adăugat una sau mai multe biblioteci de asistență la proiectul dvs., atunci, de obicei, doriți să adăugați și modulul RxBinding corespunzător.
Aceste module frățești urmează o convenție directă de numire, care facilitează identificarea bibliotecii de suport Android corespunzătoare: fiecare modul frate are pur și simplu numele bibliotecii de suport și înlocuiește com.android
cu com.jakewharton.rxbinding2: rxbinding
.
compilați com.jakewharton.rxbinding2: rxbinding-recyclerview-v7: 2.0.0 '
compilați com.jakewharton.rxbinding2: rxbinding-support-v4: 2.0.0 '
compile 'com.jakewharton.rxbinding2: rxbinding-appcompat-v7: 2.0.0'
compile 'com.jakewharton.rxbinding2: rxbinding-design: 2.0.0'
compilați com.jakewharton.rxbinding2: rxbinding-recyclerview-v7: 2.0.0 '
compile 'com.jakewharton.rxbinding2: rxbinding-leanback-v17: 2.0.0'
Dacă utilizați Kotlin în proiectele Android, atunci există și o versiune Kotlin disponibilă pentru fiecare modul RxBinding. Pentru a accesa versiunea Kotlin, pur și simplu adăugați -Kotlin
la numele bibliotecii cu care doriți să lucrați, astfel:
compile 'com.jakewharton.rxbinding2: rxbinding-design: 2.0.0'
devine:
compilați com.jakewharton.rxbinding2: rxbinding-design-kotlin: 2.0.0 '
Odată ce ați convertit a Vedere
eveniment într-un Observabil
, toate aceste evenimente sunt emise ca flux de date. După cum am văzut deja, puteți să vă abonați la aceste fluxuri și apoi să efectuați sarcina de care aveți nevoie pentru a declanșa acest anumit eveniment UI, cum ar fi afișarea Paine prajita
sau actualizarea a TextView
. Cu toate acestea, puteți aplica oricare din colecțiile enorme de operatori ale RxJava pentru acest flux observabil și chiar lanțați simultan mai mulți operatori pentru a efectua transformări complexe în evenimentele UI.
Există prea mulți operatori pentru a discuta într-un singur articol (și documentele oficiale listă oricum operatorii), dar când vine vorba de a lucra cu evenimente UI Android, există câțiva operatori care pot veni în mod deosebit de util.
debounce ()
OperatorÎn primul rând, dacă sunteți îngrijorat de faptul că un utilizator nerăbdător poate repeta în mod repetat un element UI, putând să vă confunde aplicația, atunci puteți utiliza debounce ()
operator pentru a filtra orice evenimente UI care sunt emise în succesiune rapidă.
În exemplul următor, precizez că acest buton trebuie să reacționeze la un OnClick
eveniment numai dacă a avut loc cel puțin un decalaj de 500 milisecunde de la evenimentul precedent de clic:
RxView.clicks (buton) .debounce (500, TimeUnit.MILLISECONDS) .subscribe (aVoid -> Toast.makeText (MainActivity.this, "RxView.clicks", Toast.LENGTH_SHORT) .show (););
publica()
OperatorDe asemenea, puteți utiliza funcția publica()
operator pentru a atașa mai mulți ascultători la aceeași vizualizare, ceea ce în mod tradițional a fost greu de implementat în Android.
publica()
operator convertește un standard Observabil
intr-o conectabil observabil. În timp ce un observabil obișnuit începe să emită elemente de îndată ce primul observator se abonează la el, o observabilă care poate fi conectată nu va emite nimic până când nu îi instruiți în mod explicit, aplicând conectați()
operator. Acest lucru vă oferă o fereastră de oportunitate în care să vă abonați la mai mulți observatori, fără ca observabilul să înceapă să emită elemente de îndată ce are loc primul abonament.
După ce ați creat toate abonamentele, trebuie doar să aplicați conectați()
operator și observatorul va începe să emită date către toți observatorii săi.
După cum am văzut în această serie, RxJava poate fi un instrument puternic pentru crearea de aplicații Android mai interactive, cu un număr mult mai mic de cod decât ar trebui să obțineți aceleași rezultate utilizând numai Java. Cu toate acestea, există un dezavantaj major în utilizarea aplicației RxJava în aplicațiile Android - potențialul de pierderi de memorie cauzate de abonamentele incomplete.
Aceste scurgeri de memorie apar atunci când sistemul Android încearcă să distrugă un Activitate
care conține o alergare Observabil
. Din moment ce observabilul este în desfășurare, observatorul său va menține în continuare o referință la activitate, iar sistemul nu va putea să colecteze această activitate ca un rezultat.
Din moment ce Android distruge și recreează Activitate
de fiecare dată când se modifică configurația dispozitivului, aplicația dvs. ar putea crea un duplicat Activitate
de fiecare dată când utilizatorul comută între modul portret și peisaj, precum și de fiecare dată când deschide și închide tastatura dispozitivului.
Aceste activități se vor agăța în fundal, eventual fără a fi colectate gunoi. Deoarece activitățile sunt obiecte mari, acest lucru poate duce rapid la serios probleme de gestionare a memoriei, mai ales că smartphone-urile și tabletele Android au o memorie limitată pentru a începe. Combinația dintre o scurgere de memorie mare și memoria limitată poate duce rapid la o Fara memorie eroare.
Rucurile de memorie RxJava pot avea potențialul de a face rău cu performanța aplicației dvs., dar există o bibliotecă RxAndroid care vă permite să utilizați RxJava în aplicația dvs. fără să vă faceți griji cu privire la scurgerile de memorie.
Biblioteca RxLifecycle, dezvoltată de Trello, oferă API-uri de gestionare a ciclului de viață pe care le puteți utiliza pentru a limita durata de viață a unei Observabil
la ciclul de viață al unui Activitate
sau Fragment
. Odată ce această conexiune este făcută, RxLifecycle va termina secvența observabilă ca răspuns la evenimentele ciclului de viață care apar în activitatea sau fragmentul atribuit acelei observabile. Aceasta înseamnă că puteți crea o observabilă care se termină automat ori de câte ori o activitate sau un fragment este distrus.
Rețineți că vorbim terminându- o secvență și nu se dezabonează. Deși RxLifecycle este adesea vorbit în contextul gestionării procesului de abonare / dezabonare, tehnic nu dezabonează un observator. În schimb, biblioteca RxLifecycle termină secvența observabilă emiterea fie a onComplete ()
sau onerror ()
metodă. Când vă dezabonați, observatorul nu mai primește notificări din observabil, chiar dacă observabilul încă emite elemente. Dacă aveți nevoie în mod specific de comportament de dezabonare, atunci acesta este un lucru pe care trebuie să-l implementați.
Pentru a utiliza RxLifecycle în proiectele Android, deschideți nivelul modulului build.gradle fișier și adăugați cea mai recentă versiune a bibliotecii RxLifeycle, plus biblioteca RxLifecycle Android:
dependență ... compile 'com.trello.rxlifecycle2: rxlifecycle: 2.0.1' compile 'com.trello.rxlifecycle2: rxlifecycle-android: 2.0.1'
Apoi, în Activitate
sau Fragment
unde doriți să utilizați API-urile de manipulare a ciclului de viață al bibliotecii, extindeți fie ele RxActivity
, RxAppCompatActivity
sau RxFragment
, și adăugați declarația de import corespunzătoare, de exemplu:
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity; ... public class MainActivity extinde RxAppCompatActivity
Când vine vorba de legarea unui Observabil
la ciclul de viață al unui Activitate
sau Fragment
, puteți specifica evenimentul ciclului de viață în cazul în care observabilul ar trebui să se termine sau puteți lăsa biblioteca RxLifecycle să decidă când trebuie să termine secvența observabilă.
În mod implicit, RxLifecycle va întrerupe un eveniment observabil din ciclul de viață complementar celui în care a avut loc abonamentul, deci dacă vă abonați la o observabilă în timpul activității onCreate ()
, atunci RxLifecycle va termina secvența observabilă în timpul acelei activități onDestroy ()
metodă. Dacă vă abonați în timpul a Fragment
„s onAttach ()
, atunci RxLifecycle va termina această secvență în onDetach ()
metodă.
Puteți lăsa această decizie până la RxLifecycle, utilizând RxLifecycleAndroid.bindActivity
:
ObservabilmyObservable = Observable.range (0, 25); ... @Override public void onResume () super.onResume (); myObservable .compose (RxLifecycleAndroid.bindActivitatea (ciclul de viață)) .subscribe ();
Alternativ, puteți specifica evenimentul ciclului de viață în cazul în care RxLifecycle ar trebui să rezilieze un Observabil
secvență, folosind RxLifecycle.bindUntilEvent
.
Aici, precizez că secvența observabilă ar trebui terminată onDestroy ()
:
@Override public void peResume () super.onResume (); myObservable .compose (RxLifecycle.bindUntilEvent (ciclul de viață, ActivityEvent.DESTROY)) .subscribe ();
Biblioteca finală pe care o vom analiza este RxPermissions, care a fost concepută pentru a vă ajuta să utilizați RxJava cu noul model de permisiuni introdus în Android 6.0. Această bibliotecă vă permite, de asemenea, să emiteți o solicitare de permisiune și să gestionați rezultatul permisiunii în aceeași locație, în loc să solicitați permisiunea într-un singur loc și apoi să gestionați separat rezultatele, în Activity.onRequestPermissionsResult ()
.
Începeți prin adăugarea bibliotecii RxPermissions la build.gradle fişier:
compile 'com.tbruyelle.rxpermissions2: rxpermissions: 0.9.3@aar'
Apoi, creați o instanță RxPermissions:
RxPermissions rxPermissions = noi RxPermissions (acest lucru);
Sunteți gata să începeți să faceți cereri de permisiune prin intermediul bibliotecii RxPermissions, utilizând următoarea formulă:
rxPermissions.request (Manifest.permission.READ_CONTACTS) .subscribe (acordat -> if (acordat) // Permisiunea a fost acordată // altceva // Permisiunea a fost refuzată //);
Unde să emiteți permisiunea dvs. de solicitare este crucială, deoarece există întotdeauna o șansă că găzduirea Activitate
pot fi distruse și apoi recreate în timp ce dialogul de permisiuni este afișat pe ecran, de obicei datorită unei schimbări de configurație, cum ar fi utilizatorul care se mișcă între modurile portret și peisaj. Dacă se întâmplă acest lucru, abonamentul dvs. poate să nu fie recreat, ceea ce înseamnă că nu veți fi abonat la observabilitatea RxPermissions și nu veți primi răspunsul utilizatorului la dialogul de solicitare de permisiune. Pentru a garanta că aplicația dvs. primește răspunsul utilizatorului, mereu invoca cererea dvs. în timpul unei faze de inițializare, cum ar fi Activity.onCreate ()
, Activity.onResume ()
, sau View.onFinishInflate ()
.
Nu este neobișnuit ca funcțiile să necesite mai multe permisiuni. De exemplu, trimiterea unui mesaj SMS necesită, de obicei, ca aplicația dvs. să aibă TRIMITE SMS
și READ_CONTACTS
permisiuni. Biblioteca RxPermissions oferă o metodă concisă de emitere a mai multor solicitări de permisiuni și apoi combinarea răspunsurilor utilizatorului într-o singură fals
(una sau mai multe permisiuni au fost respinse) sau Adevărat
(toate permisiunile au fost acordate) răspuns pe care puteți apoi să reacționați în consecință.
RxPermissions.getInstance (this) .request (Manifest.permission.SEND_SMS, Manifest.permission.READ_CONTACTS) .subscribe (acordat -> if (acordat) // Toate permisiunile au fost acordate // altceva // Una sau mai multe permisiuni a fost respins// );
În mod obișnuit, doriți să declanșați o solicitare de permisiune ca răspuns la un eveniment UI, cum ar fi utilizatorul atingând un element de meniu sau un buton, astfel încât RxPermissions și RxBiding sunt două biblioteci care funcționează foarte bine împreună.
Manipularea evenimentului UI ca fiind observabil și efectuarea cererii de permisiune prin RxPermissions vă permite să efectuați o mulțime de lucru cu doar câteva linii de cod:
RxView.clicks (findViewById (R.id.enableBluetooth)) .compose (RxPermissions.getInstance (this) .ensure (Manifest.permission.BLUETOOTH_ADMIN)) .subscribe (acordat -> // Butonul 'enableBluetooth' /);
După ce ați citit acest articol, aveți câteva idei despre cum să tăiați o mulțime de coduri de bare din aplicațiile Android, utilizând RxJava, pentru a gestiona toate evenimentele UI ale aplicației dvs. și pentru a vă emite cererile de permisiune prin RxPermissions. De asemenea, ne-am uitat la modul în care puteți folosi RxJava în orice Android Activitate
sau Fragment
, fără a fi nevoie să vă faceți griji cu privire la scurgeri de memorie care pot fi cauzate de abonamentele incomplete.
Am explorat câteva dintre cele mai populare și utile biblioteci RxJava și RxAndroid din această serie, dar dacă sunteți dornici să vedeți ce altceva RxJava are de oferit dezvoltatorilor Android, verificați câteva dintre celelalte biblioteci RxAndroid. Veți găsi o listă completă de biblioteci suplimentare RxAndroid de la GitHub.
Între timp, verificați câteva dintre celelalte postări de dezvoltare Android de aici, pe Envato Tuts+!