RxJava 2.0 er et populært reaktivt programmeringsbibliotek som har hjulpet utallige Android-utviklere til å lage svært lydhøre apps, ved hjelp av mindre kode og mindre kompleksitet, spesielt når det gjelder å administrere flere tråder.
Hvis du er en av de mange utviklerne som har skiftet til Kotlin, betyr det ikke at du må gi opp på RxJava!
I den første delen av denne serien viste jeg deg hvordan du kan flytte fra programmering med RxJava 2.0 i Java til programmering med RxJava i Kotlin. Vi så også på hvordan du kan forkaste boilerplate fra prosjektene dine ved å utnytte RxKotlins utvidelsesfunksjoner og hemmeligheten til å dodging SAM-konverteringsproblemet som mange utviklere opplever når de først begynner å bruke RxJava 2.0 med Kotlin.
I denne andre trensen vil vi konsentrere oss om hvordan RxJava kan bidra til å løse problemene du vil støte på i virkelige Android-prosjekter, ved å lage et reaktivt Android-program ved hjelp av RxJava 2.0, RxAndroid og RxBinding.
I vår Reactive Programming med RxJava og RxKotlin artikkelen, opprettet vi noen enkle observer
og observatører
som skriver ut data til Android Studio logcat-men dette er ikke hvordan du bruker RxJava i den virkelige verden.
I denne artikkelen skal jeg vise deg hvordan du bruker RxJava til å lage en skjerm som brukes i utallige Android-applikasjoner: den klassiske Melde deg på skjerm.
Hvis appen din har noen type oppmeldingsopplevelse, da vil det vanligvis ha strenge regler om hva slags informasjon det aksepterer. For eksempel må passordet kanskje overstige et bestemt antall tegn, eller e-postadressen må være i et gyldig e-postformat.
Mens du kunne sjekk brukerens innspilling når de treffer Melde deg på knappen, dette er ikke den beste brukeropplevelsen, ettersom den lar dem åpne for å sende inn informasjon som tydeligvis aldri vil bli akseptert av søknaden din.
Det er langt bedre å overvåke brukeren mens de skriver, og deretter gi dem en heads-up så snart det blir klart at de skriver inn informasjon som ikke oppfyller appens krav. Ved å gi denne typen levende og kontinuerlig tilbakemelding, gir du brukeren muligheten til å rette opp feilene sine før treffer det Melde deg på knapp.
Mens du kunne overvåke brukeraktivitet ved bruk av vanilla Kotlin, kan vi levere denne funksjonaliteten ved å bruke mye mindre kode ved å anskaffe hjelp av RxJava, pluss noen andre relaterte biblioteker.
La oss begynne med å bygge vårt brukergrensesnitt. Jeg skal legge til følgende:
EditTexts
, hvor brukeren kan skrive inn sin e-postadresse (enterEmail
) og passord (oppgi passord
).TextInputLayout
wrappers, som vil omgir vår enterEmail
og oppgi passord
EditTexts
. Disse pakkene vil vise en advarsel når brukeren skriver inn en e-postadresse eller et passord som ikke oppfyller kravene til appen vår.Her er mitt ferdige layout:
Du kan kopiere / lime inn dette i appen din hvis du vil, eller du kan bare laste ned prosjektkildekoden fra vår GitHub repo.
La oss nå se hvordan vi kan bruke RxJava, pluss noen tilknyttede biblioteker, til å overvåke brukerinngang og gi tilbakemelding i sanntid.
Jeg skal takle Melde deg på skjermen i to deler. I den første delen viser jeg deg hvordan du bruker RxBinding-biblioteket til å registrere og svare på endringshendelser. I den andre delen vil vi opprette noen transformasjonsfunksjoner som validerer brukerens innspill, og deretter vise en feilmelding der det er aktuelt.
Opprett et nytt prosjekt med de ønskede innstillingene, men når du blir bedt om det, må du kontrollere at du velger Inkluder Kotlin Support avkrysnings.
I denne delen implementerer vi følgende funksjonalitet:
enterEmail
felt.Skål
. RxBinding er et bibliotek som gjør det enklere å konvertere et bredt spekter av brukergrensesnitt hendelser til observables, på hvilket tidspunkt kan du behandle dem akkurat som alle andre RxJava datastrømmer.
Vi skal overvåke tekstendringshendelser ved å kombinere RxBinding's widget.RxTextView
med afterTextChangeEvents
metode, for eksempel:
RxTextView.afterTextChangeEvents (enterEmail)
Problemet med å behandle tekstendringshendelser som datastrømmer er det som utgangspunktet både enterEmail
og enterPassword EditTexts
vil være tom, og vi vil ikke at vår app skal reagere på denne tomme tilstanden som om den er den første datautslipp i strømmen. RxBinding løser dette problemet ved å gi en skipInitialValue ()
metode, som vi vil bruke til å instruere hver Observer å ignorere strømmen sin innledende verdi.
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue ()
Jeg ser på RxBinding-biblioteket mer detaljert i min RxJava 2 for Android Apps-artikkel.
.debounce ()
OperatørFor å levere den beste brukeropplevelsen må vi vise alle relevante passord eller e-postvarsler etter at brukeren har skrevet inn, men før de treffer Melde deg på knapp.
Uten RxJava ville det ved å identifisere dette smale tidsvinduet vanligvis kreve at vi implementerer en Timer
, men i RxJava trenger vi bare å bruke debounce ()
operatør til datastrømmen vår.
Jeg skal bruke debounce ()
operatør for å filtrere ut alle tekstendringshendelser som skjer i rask rekkefølge, dvs. når brukeren fortsatt skriver. Her ignorerer vi alle tekstendringshendelser som skjer i det samme 400-millisekund-vinduet:
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS)
AndroidSchedulers.mainThread ()
RxAndroid bibliotekets AndroidSchedulers.mainThread
gir oss en enkel måte å bytte til Android's viktigste viktige hovedbruker-tråd.
Siden det bare er mulig å oppdatere Android-brukergrensesnittet fra hovedbruddstråden, må vi sørge for at vi er i denne tråden før vi forsøker å vise e-post eller passordadvarsler, og før vi viser vår Skål
.
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ())
For å motta dataene som sendes ut av enterEmail
, vi må abonnere på det:
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe
Til slutt vil vi at vårt program skal svare på endringer i tekstendringer ved å validere brukerens innspill, men for å holde ting grei, på dette punktet skal jeg bare vise en Skål
.
Koden din skal se slik ut:
importer android.support.v7.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import com.jakewharton.rxbinding2.widget.RxTextView importere kotlinx.android.synthetic.main.activity_main. * Import io.reactivex.android .schedulers.AndroidSchedulers importere java.util.concurrent.TimeUnit klasse MainActivity: AppCompatActivity () overstyr moro 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 (dette, "400 millisekunder siden siste tekstendring", Toast.LENGTH_SHORT) .show ()
Siden vi bruker noen forskjellige biblioteker, må vi åpne prosjektet vårt build.gradle fil og legg til RxJava, RxBinding og RxAndroid som prosjektavhengigheter:
avhengighet implementering fileTree (dir: 'libs', inkluderer: ['* .jar']) implementering "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementering "com.android.support:design:28.0. 0-alfa1 'implementering' com.android.support:appcompat-v7:28.0.0-alpha1 'implementering' com.android.support.constraint: begrensningsoppsett: 1.1.0 '// Legg til RxJava-avhengigheten // implementering' io.reactivex.rxjava2: rxjava: 2.1.9 '// Legg til RxAndroid-avhengigheten // implementering' io.reactivex.rxjava2: rxandroid: 2.0.2 '// Legg til RxBinding-avhengigheten // implementering' com.jakewharton.rxbinding2: rxbinding: 2.1.1 '
Du kan teste denne delen av prosjektet ditt ved å installere det på din fysiske Android-smarttelefon eller nettbrett, eller Android Virtual Device (AVD). Velg enterEmail
EditText
og begynn å skrive; en Skål
skal vises når du slutter å skrive.
Deretter må vi legge ned noen grunnregler om hva slags innspill vår søknad vil akseptere, og deretter sjekke brukerens innspill mot dette kriteriet og vise en feilmelding der det er aktuelt.
Å sjekke brukerens e-post eller passord er en multi-trinns prosess, så for å gjøre koden enklere å lese, skal jeg kombinere alle disse trinnene i egen transformasjonsfunksjon.
Her er starten på validateEmail
transformasjonsfunksjon:
// Definer en ObservableTransformer. Inngang og utdata må være en streng // privat val validateEmailAddress = ObservableTransformerobserverbar -> // Bruk flatmappe til å bruke en funksjon til hvert element som er utgitt av Observable // observable.flatMap // Trim hvitt mellomrom ved begynnelsen og slutten av brukerens inntasting // Observable.just (it) .map it.trim () // Sjekk om innspillet samsvarer med Android-e-postmønsteret // .filter Patterns.EMAIL_ADDRESS.matcher (det) .matches ()
I koden ovenfor bruker vi filter()
operatør for å filtrere den observerbare produksjonen basert på om den samsvarer med Android Patterns.EMAIL_ADDRESS
mønster.
I neste del av transformasjonsfunksjonen må vi spesifisere hva som skjer hvis inngangen ikke stemmer overens med EPOSTADRESSE
mønster. Som standard utløser hver uopprettelig feil en samtale til onError ()
, som avslutter datastrømmen. I stedet for å avslutte strømmen, vil vi at vårt program skal vise en feilmelding, så jeg skal bruke onErrorResumeNext
, som instruerer Observable å svare på en feil ved å sende kontroll til en ny Observerbar, i stedet for å påkalle onError ()
. Dette gjør at vi kan vise vår egendefinerte feilmelding.
// Hvis brukerens inntasting ikke samsvarer med e-postmønsteret, kaster du en feil // .singleOrError () .onErrorResumeNext hvis (det er NoSuchElementException) Single.error (Unntak ("Vennligst skriv inn en gyldig e-postadresse")) ellers Single.error (it) .toObservable ()
Det siste trinnet er å bruke denne transformasjonsfunksjonen til e-datastrømmen, ved hjelp av .komponere ()
operatør. På dette punktet, din MainActivity.kt bør se slik ut:
importer android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns importere io.reactivex.Observable import io.reactivex.ObservableTransformer import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers importere kotlinx.android.synthetic.main.activity_main. * Importer java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView klasse MainActivity: AppCompatActivity () overstyr moro 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, / / Sørg for at vi er i Android's hovedbruker-tråd // TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validateEmailAddress) .compose (retryWhenError passwordError.error = it.message) .subscribe () // Hvis appen opplever en feil, prøv deretter igjen // privat inline fu n retryWhenError (crossinline onError: (ex: Throwable) -> Enhet): ObservableTransformer= ObserverbarTransformer observerbar -> observerbar.retryNår feil -> // Bruk flatmap () operatøren til å flate alle utslippene til en enkelt Observerbar // feil. Mappe onError (det) Observable.just ("") / / Definer en ObservableTransformer, der vi skal utføre e-valideringen // private val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Sjekk om brukerinngangen samsvarer med Android-e-postmønsteret // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Hvis brukerens inntasting ikke samsvarer med e-postmønsteret, kaster du en feil // .singleOrError () .onErrorResumeNext hvis (det er NoSuchElementException) Single.error (Unntak ("Vennligst skriv inn en gyldig e-postadresse" )) ellers Single.error (it) .toObservable ()
Installer dette prosjektet på Android-enheten din eller AVD, og du vil oppdage at e-postdelen av Melde deg på skjermen kontrollerer nå innspillingen din vellykket. Prøv å skrive inn noe annet enn en e-postadresse, og appen vil advare deg om at dette ikke er en gyldig inngang.
På dette punktet har vi en fullt fungerende enterEmail
felt og implementering oppgi passord
er for det meste bare et tilfelle av å gjenta de samme trinnene.
Faktisk er den eneste store forskjellen at vår validatePassword
transformasjonsfunksjonen må sjekke for forskjellige kriterier. Jeg skal spesifisere at brukerens passordinngang må være minst 7 tegn lang:
.filter it.length> 7
Etter å ha gjentatt alle de forrige trinnene, er det fullført MainActivity.kt bør se slik ut:
importer android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns importere io.reactivex.Observable import io.reactivex.ObservableTransformer import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers importere kotlinx.android.synthetic.main.activity_main. * Importer java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView klasse MainActivity: AppCompatActivity () overstyr moro onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Svar på tekstendringshendelser i enterEmail // RxTextView.afterTextChangeEvents (enterEmail) // Hopp over enterEmail's initial, empty state // .skipInitialValue () // Transform dataene som sendes ut / / .map emailError.error = null // Konverter brukerinngangen til en streng // it.view (). text.toString () // Ignorer alle utslipp som forekommer innen en tidsperiode på 400 millisekunder // .debounce (400 , / / Pass på at vi er i Android hovedbruker-tråden // Tim eUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) // Bruk validateEmailAddress transformation funksjonen // .compose (validateEmailAddress) // Bruk retryWhenError transformasjonsfunksjonen // .compose (retryWhenError emailError.error = it.message) .subscribe () // Skyll og gjenta for enterPassword EditText // RxTextView.afterTextChangeEvents (enterPassword) .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 () // Hvis appen opplever en feil, prøv deretter igjen / / private inline fun retryWhenError (crossinline onError: (ex: Throwable) -> Enhet): ObservableTransformer= ObserverbarTransformer observerbar -> observerbar.retryNår feil -> /// Bruk flatmap () operatøren til å flate alle utslippene til en enkelt Observerbar // feil. Mappe onError (det) Observable.just ("") // Definer vår ObservableTransformer og spesifiser at inngang og utdata må være en streng // privat val validatePassword = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Bare tillat passord som er minst 7 tegn lange // .filter it.length> 7 // Hvis passordet er mindre enn 7 tegn, og deretter kaste en feil // .singleOrError () // Hvis det oppstår en feil ... // .onErrorResumeNext hvis (det er NoSuchElementException) // Vis følgende melding i passordet Feil TextInputLayout // Single. feil (Unntak ("Ditt passord må være 7 tegn eller mer")) ellers Single.error (it) .toObservable () // Definer en ObservableTransformer, der vi skal utføre e- val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Sjekk om brukerinngangen samsvarer med Android-e-postmønsteret // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Hvis brukerens inntasting ikke samsvarer med e-postmønsteret ... // .singleOrError () .onErrorResumeNext hvis (det er NoSuchElementException) //// Vis følgende melding i e-postError TextInputLayout // Single.error ( Unntak ("Vennligst skriv inn en gyldig e-postadresse")) ellers Single.error (it) .toObservable ()
Installer dette prosjektet på Android-enheten din eller AVD, og eksperimenter med å skrive inn i enterEmail
og oppgi passord
Enger. Hvis du oppgir en verdi som ikke oppfyller appens krav, vil den vise den tilsvarende advarselsmeldingen, uten du må trykke på Melde deg på knapp.
Du kan laste ned dette komplette prosjektet fra GitHub.
I denne artikkelen så vi på hvordan RxJava kan bidra til å løse de virkelige problemene du møter når du utvikler dine egne Android-applikasjoner, ved å bruke RxJava 2.0, RxBinding og RxAndroid for å lage en Melde deg på skjerm.
For mer bakgrunnsinformasjon om RxJava-biblioteket, må du sjekke ut vår Kom i gang med RxJava 2.0-artikkelen.