RxJava 2.0 este o bibliotecă populară de programare reactivă care a ajutat numeroși dezvoltatori Android să creeze aplicații foarte receptive, folosind mai puțin cod și mai puțin complexitate, mai ales când vine vorba de gestionarea mai multor fire.
Dacă sunteți unul dintre mulți dezvoltatori care au făcut trecerea la Kotlin, atunci nu înseamnă că trebuie să renunți la RxJava!
În prima parte a acestei serii, ți-am arătat cum să te miști de la programare cu RxJava 2.0 în Java la programarea cu RxJava în Kotlin. De asemenea, ne-am uitat la cum să eliminați boilerplate din proiectele dvs. utilizând avantajele funcțiilor de extensie ale RxKotlin și secretul de a eschiva problema conversiei SAM pe care mulți dezvoltatori o întâlnesc când încep să utilizeze RxJava 2.0 cu Kotlin.
În această a doua tranșă, ne vom concentra pe modul în care RxJava vă poate ajuta să rezolvați problemele pe care le veți întâlni în proiectele Android live, creând o aplicație reactivă Android utilizând RxJava 2.0, RxAndroid și RxBinding.
În programarea noastră reactivă cu articolele RxJava și RxKotlin, am creat câteva simple observabilelor
și observatorii
care imprimă date la Android Studio logcat-dar acest lucru nu este modul în care veți folosi RxJava în lumea reală.
În acest articol, vă voi arăta cum să utilizați RxJava pentru a crea un ecran folosit în nenumărate aplicații Android: clasicul Inscrie-te ecran.
Dacă aplicația dvs. are orice un fel de experiență de înscriere, atunci vor avea reguli stricte cu privire la tipul de informații pe care le acceptă. De exemplu, poate că parola trebuie să depășească un anumit număr de caractere sau adresa de e-mail trebuie să fie într-un format de e-mail valid.
In timp ce tu ar putea verificați intrarea utilizatorului după ce a lovit Inscrie-te butonul nu este cea mai bună experiență a utilizatorului, deoarece îi lasă deschisă să furnizeze informații care, în mod clar, nu vor fi acceptate niciodată de aplicația dvs..
Este mult mai bine să monitorizezi utilizatorul pe măsură ce scrie, apoi să-i dai un heads-up de îndată ce devine clar că introduc informații care nu corespund cerințelor aplicației tale. Oferind acest tip de reacții live și în curs de desfășurare, le oferiți utilizatorului posibilitatea de a-și corecta greșelile inainte de lovind asta Inscrie-te buton.
In timp ce tu ar putea monitorizați activitatea utilizatorilor folosind vanilla Kotlin, putem furniza această funcționalitate folosind coduri mult mai mici prin înscrierea ajutorului RxJava, plus câteva alte biblioteci conexe.
Să începem prin construirea interfeței cu utilizatorul. Voi adăuga următoarele:
EditTexts
, în cazul în care utilizatorul poate introduce adresa de e-mail (introduceți adresa de email
) și parola (Introdu parola
).TextInputLayout
împachetări, care ne vor înconjura introduceți adresa de email
și Introdu parola
EditTexts
. Aceste pachete vor afișa un avertisment ori de câte ori utilizatorul introduce o adresă de e-mail sau o parolă care nu respectă cerințele aplicației noastre.Iata aspectul final:
Puteți să copiați / inserați acest lucru în aplicația dvs. dacă doriți sau puteți să descărcați codul sursă al proiectului din repo GitHub.
Acum, să ne uităm la modul în care putem folosi RxJava, plus câteva biblioteci conexe, pentru a monitoriza datele introduse de utilizatori și pentru a oferi feedback în timp real.
Voi aborda problema Inscrie-te ecran în două părți. În prima secțiune, vă vom arăta cum să utilizați biblioteca RxBinding pentru a vă înregistra și a răspunde la evenimentele de modificare a textului. În cea de-a doua secțiune, vom crea câteva funcții de transformare care validă intrarea utilizatorului și apoi vom afișa un mesaj de eroare acolo unde este cazul.
Creați un nou proiect cu setările alese, dar când vi se solicită, asigurați-vă că selectați Includeți suportul Kotlin Caseta de bifat.
În această secțiune vom implementa următoarele funcții:
introduceți adresa de email
camp.Paine prajita
. RxBinding este o bibliotecă care ușurează conversia unei game largi de evenimente UI în Repere, moment în care puteți să le tratați ca orice alt flux de date RxJava.
Vom monitoriza evenimentele de schimbare a textului, prin combinarea lui RxBinding widget.RxTextView
cu afterTextChangeEvents
metoda, de exemplu:
RxTextView.afterTextChangeEvents (enterEmail)
Problema cu tratarea evenimentelor de schimbare a textului ca fluxuri de date este că inițial atât introduceți adresa de email
și introduceți paragraful EditTexts
va fi gol și nu vrem ca aplicația noastră să reacționeze la această stare goală ca și cum ar fi prima emisie de date din flux. RxBinding rezolvă această problemă furnizând a skipInitialValue ()
metodă pe care o vom folosi pentru a instrui fiecare observator să ignore valoarea inițială a fluxului.
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue ()
Mă uit la biblioteca RxBinding în detaliu în articolul meu RxJava 2 pentru articolul Android Apps.
.debounce ()
OperatorPentru a oferi cea mai bună experiență a utilizatorilor, trebuie să afișăm toate avertismentele relevante pentru parolă sau e-mail după ce utilizatorul a terminat de scris, dar înainte de a lovi Inscrie-te buton.
Fără RxJava, identificarea acestei ferestre înguste de timp ar necesita în mod obișnuit să implementăm o Cronometrul
, dar în RxJava trebuie doar să aplicăm debounce ()
operator în fluxul nostru de date.
O să folosesc debounce ()
operator pentru a filtra toate evenimentele de schimbare a textului care se întâmplă în succesiune rapidă, adică când utilizatorul încă scrie. Aici, ignorăm toate evenimentele de modificare a textului care se produc în aceeași fereastră de 400 de milisecunde:
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS)
AndroidSchedulers.mainThread ()
Biblioteca RxAndroid AndroidSchedulers.mainThread
ne oferă o modalitate ușoară de a trece la firul principal al UI principal al Android.
Din moment ce este posibilă actualizarea UI-ului Android de la firul principal al UI, trebuie să ne asigurăm că suntem în acest thread înainte de a încerca să afișăm avertismente prin e-mail sau parolă și înainte de a ne afișa Paine prajita
.
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .deblicare (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ())
Pentru a primi datele emise de către introduceți adresa de email
, trebuie să ne abonați la acesta:
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .deblicare (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe
În cele din urmă, dorim ca aplicația noastră să răspundă evenimentelor de modificare a textului, validând intrarea utilizatorului, dar pentru a ajuta la menținerea unor lucruri simple, în acest moment, Paine prajita
.
Codul dvs. ar trebui să arate astfel:
importați android.support.v7.app.AppCompatActivity importați android.os.Bundle import android.widget.Toast import com.jakewharton.rxbinding2.widget.RxTextView import kotlinx.android.synthetic.main.activity_main. * import io.reactivex.android .schedulers.AndroidSchedulers importa clasa java.util.concurrent.TimeUnit MainActivity: AppCompatActivity () suprascris fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) RxTextView.afterTextChangeEvents (enterEmail). skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe Toast.makeText (aceasta, "400 milisecunde de la ultima modificare a textului", Toast.LENGTH_SHORT) .show ()
Deoarece folosim câteva biblioteci diferite, trebuie să deschidem proiectul nostru build.gradle și adăugați RxJava, RxBinding și RxAndroid ca dependențe de proiect:
dependente implementare fileTree (dir: 'libs', include: ['* .jar']) implementare "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementare "com.android.support:design:28.0. 0-alpha1 "implementare" com.android.support:appcompat-v7:28.0.0-alpha1 "implementare" com.android.support.constraint: constrângere-layout: 1.1.0 '// Adăugați dependența RxJava // implementare' io.reactivex.rxjava2: rxjava: 2.1.9 '// Adăugați dependența RxAndroid // implementation' io.reactivex.rxjava2: rxandroid: 2.0.2 '// Adăugați dependența RxBinding // implementare' com.jakewharton.rxbinding2: Îmbinare: 2.1.1 '
Puteți testa această parte a proiectului dvs., instalându-l pe telefonul smartphone sau pe tableta dvs. Android sau pe dispozitivul virtual Android (AVD). Selectează introduceți adresa de email
Editează textul
și începeți să tastați; A Paine prajita
ar trebui să apară atunci când opriți tastarea.
Apoi, trebuie să stabilim câteva reguli de bază privind tipul de intrare pe care aplicația noastră o va accepta și apoi să verifice datele introduse de utilizator în baza acestor criterii și să afișeze un mesaj de eroare acolo unde este cazul.
Verificarea e-mailului sau a parolei utilizatorului este un proces în mai multe etape, astfel încât pentru a face codul nostru mai ușor de citit, voi combina toți acești pași în propriul lor funcția de transformare.
Iată începutul validati emailul
funcția de transformare:
// Definirea unui ObservableTransformer. Intrarea și ieșirea trebuie să fie un șir // private val validateEmailAddress = ObstableTransformerobservable -> // Folosiți flatMap pentru a aplica o funcție pentru fiecare element emis de observable.flatMap // Trim orice spațiu alb la începutul și la sfârșitul intrării utilizatorului // Observable.just (it) .map it.trim () // Verificați dacă intrarea se potrivește modelului de e-mail Android // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ()
În codul de mai sus, folosim filtru()
operator pentru a filtra ieșirea lui Observable pe baza faptului că se potrivește cu Android Patterns.EMAIL_ADDRESS
model.
În următoarea parte a funcției de transformare, trebuie să specificăm ce se întâmplă dacă intrarea nu corespunde ADRESA DE EMAIL
model. Implicit, fiecare eroare nerecuperabilă va declanșa un apel către onerror ()
, care întrerupe fluxul de date. În loc de a termina fluxul, dorim ca aplicația noastră să afișeze un mesaj de eroare, așa că o să folosesc onErrorResumeNext
, care instruiește Observantul să răspundă la o eroare prin trecerea controlului la o nouă observabilă, mai degrabă decât invocând onerror ()
. Acest lucru ne permite să afișăm mesajul nostru de eroare personalizat.
// Dacă intrarea utilizatorului nu se potrivește cu modelul de e-mail, aruncați o eroare // .singleOrError () .onErrorResumeNext dacă (este NoSuchElementException) Single.error (Excepție ("Introduceți o adresă de e-mail validă")) altceva Single.error (it) .toObservable ()
Pasul final este de a aplica această funcție de transformare în fluxul de date de e - mail, folosind .Compune()
operator. În acest moment, dvs. MainActivity.kt ar trebui să arate ceva de genul:
importați android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns import io.reactivex.Observabil import io.reactivex.ObservableTransformer import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers importul plugin-ului de tip klinux.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView clasa MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .map emailError.error = null it.view () text.toString () .debounce (400, suntem în firul principal al aplicației Android // TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validateEmailAddress) .compose (retryWhenError passwordError.error = it.message) .subscribe () aplicația întâmpină o eroare, apoi încercați din nou // private inline fu n retryWhenError (crossinline onError: (ex: Throwable) -> Unitate): ObservableTransformer= ObservableTransformer observable -> observable.retryDaca erorile -> // Folositi operatorul flatmap () pentru a alinia toate emisiile intr-o singura observabila // errors.flatMap onError (it) Observable.just (" / Definește un ObservableTransformer, unde vom efectua validarea e-mailului // private val validateEmailAddress = ObstableTransformer observable -> observable.flatMap observable.just (it) .map it.trim () // Verificați dacă intrarea utilizatorului corespunde modelului de e-mail // // filtrul Patterns.EMAIL_ADDRESS.matcher (it) .matches ) // Dacă intrarea utilizatorului nu se potrivește cu modelul de e-mail, aruncați o eroare // .singleOrError () .onErrorResumeNext dacă (este NoSuchElementException) Single.error (Excepție ("Introduceți o adresă de e-mail validă" )) altceva Single.error (it) .toObservable ()
Instalați acest proiect pe dispozitivul Android sau pe AVD și veți găsi că partea de e-mail a dispozitivului Inscrie-te ecranul vă verifică intrarea cu succes. Încercați să introduceți altceva decât o adresă de e-mail și aplicația vă va avertiza că aceasta nu este o intrare validă.
În acest moment, avem o funcționare pe deplin introduceți adresa de email
domeniul și implementarea Introdu parola
este mai mult decât un caz de repetare a acelorași pași.
De fapt, singura diferență majoră este aceea că noi validatePassword
funcția de transformare trebuie să verifice diferite criterii. Voi specifica faptul că introducerea parolei utilizatorului trebuie să aibă cel puțin 7 caractere:
.filtru it.length> 7
Dupa repetarea tuturor pasilor anteriori, completat MainActivity.kt ar trebui să arate ceva de genul:
importați android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns import io.reactivex.Observabil import io.reactivex.ObservableTransformer import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers importul plugin-ului de tip klinux.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView clasa MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCreate (saveInstanceState) setContentView (R.layout.activity_main) // Răspundeți la evenimentele de schimbare a textului din enterEmail // RxTextView.afterTextChangeEvents (enterEmail) // Săriți starea inițială inițială, empty // // skipInitialValue () // Transformați datele emise / / .map emailError.error = null // Convertiți intrarea utilizatorului într-un String // it.view (). text.toString () // Ignorați toate emisiile care apar în intervalul de timp de 400 milisecunde //debounce (400 , // Asigurați-vă că suntem în firul principal al aplicației Android // Tim eUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) // Aplicați funcția de transformare validateEmailAddress // .compose (validateEmailAddress) // Aplicați funcția de transformare retryWhenError // .compose (retryWhenError emailError.error = it.message) .subscribe () // Clătiți și repetați pentru enterPassword EditText // RxTextView.afterTextChangeEvents (introduceți parola) .skipInitialValue () .map passwordError.error = null it.view () text.toString () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validatePassword) .compose (retryWhenError passwordError.error = it.message) .subscribe () // Dacă aplicația întâmpină o eroare, / private inline distracție retryWhenError (crossinline onError: (ex: Throwable) -> Unitate): ObservableTransformer= ObservableTransformer observable -> observable.retryWhen errors -> /// Utilizați operatorul flatmap () pentru a alinia toate emisiile într-o singură observabilă // errors.flatMap onError (it) Observable.just (") // Definirea lui ObservableTransformer și specificarea că intrarea și ieșirea trebuie să fie un șir // private val validatePassword = ObstableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Permiteți parole care au cel puțin 7 caractere lungi // .filter it.length> 7 // Dacă parola este mai mică de 7 caractere, apoi aruncați o eroare // .singleOrError () // Dacă apare o eroare ... // .onErrorResumeNext if (este NoSuchElementException) // Afișați următorul mesaj în parolaError TextInputLayout // Single. eroare (Excepție ("Parola dvs. trebuie să fie de 7 caractere sau mai mult")) altceva Single.error (it) .toObservable () // Definiți un ObservableTransformer unde vom efectua validarea // private val validateEmailAddress = ObservableTransformer observable -> observable.flatMap observable.just (it) .map it.trim () // Verificați dacă intrarea utilizatorului corespunde modelului de e-mail // // filtrul Patterns.EMAIL_ADDRESS.matcher (it) .matches ) // Dacă intrarea utilizatorului nu se potrivește cu modelul de email ... // .singleOrError () .onErrorResumeNext dacă (este NoSuchElementException) //// Afișați următorul mesaj în emailError TextInputLayout // Single.error ( Excepție ("Introduceți o adresă de e-mail validă")) altceva Single.error (it) .toObservable ()
Instalați acest proiect pe dispozitivul Android sau AVD și experimentați cu tastarea în introduceți adresa de email
și Introdu parola
câmpuri. Dacă introduceți o valoare care nu îndeplinește cerințele aplicației, va afișa mesajul de avertizare corespunzător, fără trebuie să atingeți Inscrie-te buton.
Puteți descărca acest proiect complet de la GitHub.
În acest articol, ne-am uitat la modul în care RxJava vă poate ajuta să rezolvați problemele din lumea reală pe care le veți întâlni atunci când vă dezvoltați propriile aplicații Android, utilizând RxJava 2.0, RxBinding și RxAndroid pentru a crea o Inscrie-te ecran.
Pentru mai multe informații de bază despre biblioteca RxJava, asigurați-vă că verificați articolul Get Started With RxJava 2.0.