Bedste RxJava-operatører til REST-applikationer i Android

Der er mange forskellige operatører i standard RxJava-pakken. Nogle af dem er virkelig robuste og komplicerede at bruge, andre temmelig enkle. Men der er én ting, som mange RxJava-operatører har til fælles:

De fleste af dem vil du aldrig bruge

Som dagligdags Android-udvikler, der laver alle tingene i RxJava, bestræbte jeg mig mange gange på at bruge zip () -operatør, og hver gang jeg ikke kunne gøre det. Jeg har altid fundet noget bedre end det, eller en situation, som denne operatør ikke vil dække. Jeg siger ikke, at zip () overhovedet ikke har nogen brug, nogen måske kan lide det, og hvis det fungerer for dig - Det er fantastisk. Men lad os diskutere nogle operatører, som jeg synes er meget nyttige, og de er fantastiske & lette at bruge i REST-baseret applikation.

Og her er de:

  • del()
  • replay (1) .refCount ()
Hvis du allerede ved, hvad de laver, kan du lige så godt efterlade et klap for mig og være færdig med at læse på dette tidspunkt.

Varm eller kold?

En af de vigtigste ting, der ofte er svært at forstå, er, om det observerbare er varmt eller koldt. Der har været mange gode artikler, der forklarer det, og jeg har ikke til hensigt at gøre det igen, i stedet vil jeg vise dig eksempler på, hvordan det fungerer i praksis.

Når alt kommer til alt betyder det noget, om dit opkald er noget observerbart varmt, koldt eller varmt?

Ingen.

Det eneste, der betyder noget, er: hvis det gør jobbet.

Generelt har du muligvis brug for to slags observerbare ting:

  • observerbar, der husker sidst udsendte værdi, og udsender den til alle nye abonnenter,
  • observerbar, som ikke husker den sidst udsendte værdi.

Tal er billig. Vis mig koden.

Lad os sige, at vi i vores applikation vil downloade nogle data og vise dem. Lad os forestille os den nemmeste måde at gøre det på:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
         .subscribe {view.update (it)}
         .subscribe {view.update (it)}

Der. Lad os nu tilføje fejlhåndtering:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
usersObservable
         .filter {it.isNotError ()}
         .subscribe {view.update (it)}
usersObservable
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}

Store. Men lad os også tilføje en status og en tom liste til den bedste UX:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
usersObservable
         .filter {it.isNotError ()}
         .subscribe {view.update (it)}
usersObservable
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}
usersObservable
         .map (falsk)
         .startWith (sand)
         .subscribe {progressLoading.visibility = it}
usersObservable
         .map (it.isEmpty ())
         .startWith (falsk)
         .subscribe {emptyMessage.visibility = it}

Nu… er der noget galt i denne kode? Vi kan teste det.

@Prøve
sjov test () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

I ovenstående test er derObservable.just () i stedet for en REST-anmodning. Hvorfor fusionere med (aldrig ())? Fordi vi ikke ønsker, at vores observerbare skal udføres, før hver abonnent har en chance for at abonnere på det. En lignende situation (observerbar endelig slutning) kan bemærkes, når en anmodning udløses af brugerinput-input. Denne sag vil blive behandlet senere i artiklen. Også de fire observerbare ting, der blev brugt i det foregående eksempel, blev forenklet til kun at abonnere (). Vi kan ignorere planlægningsdel, da alt sker i en tråd. Det endelige resultat er:

[bruger1, bruger2]
[bruger1, bruger2]
[bruger1, bruger2]
[bruger1, bruger2]

Hvert abonnement på userOrError observerbart har udløst println (), hvilket betyder, at vi i den virkelige applikation netop udløste fire anmodninger i stedet for en. Dette kan være en meget farlig situation. Forestil dig, at i stedet for en potentielt ufarlig GET-anmodning, ville vi foretage POST eller ringe til en anden metode, der ændrer status for data eller applikation. Den samme anmodning udføres fire gange, og der oprettes for eksempel fire identiske indlæg eller kommentarer.

Heldigvis kan vi nemt løse det ved at tilføje gentagelse (1) .refCount ().

@Prøve
sjove `test replay refCount operatorer` () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}
            .replay (1)
            .refCount ()

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

Resultatet af denne test er:

[bruger1, bruger2]

Fantastisk, vi har med succes delt vores abonnement mellem alle abonnenter. Nu er der ingen trussel om at fremsætte unødvendige flere anmodninger. Lad os prøve det samme observerbare med share () operatør i stedet for gentagelse (1) .refCount ().

@Prøve
sjovt "test share operator" () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}
            .del()

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

Resultatet er overraskende (eller ikke) det samme som tidligere:

[bruger1, bruger2]

For at se forskel mellem deling () og gentagelse (1) .refCount () lad os lave to tests til. Denne gang ringer vi til vores falske anmodning, når vi har fået klikbegivenhed fra brugeren. Klikbegivenhed bliver spottet af aPublishSubject. Yderligere linje: doOnNext {println ("1")} viser, hvilken abonnent der fik begivenheden fra usersOrError

Den første test bruger share () og den anden gentagelse (1) .refCount.

@Prøve
sjovt "testdelingsoperatør med klik" () {
    val clickEvent = PublishSubject.create  ()

    val usersOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1", "user2"))}
            .del()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    usersOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // udfør click

    usersOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

Resultat:

1
2

@Prøve
sjove `test replay refCount operatorer med klik` () {
    val clickEvent = PublishSubject.create  ()

    val usersOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1", "user2"))}
            .replay (1)
            .refCount ()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    usersOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // udfør click

    usersOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

Resultat:

1
2
3
4

Konklusion

Både share () og replay (1) .refCount () er vigtige operatører til at håndtere REST-anmodninger og meget mere. Hver gang du har brug for det samme, der kan observeres flere steder, er det den bedste vej at gå. Bare tænk, hvis du vil have din observerbare til at huske den seneste begivenhed og videregive den til hver nye abonnent, eller måske er du interesseret i engangsbearbejdning. Her er nogle eksempler på applikationer i det virkelige liv:

  • getUsers (), getPosts () eller lignende observerbar, der bruges til at hente dataene, vil sandsynligvis usereplay (1) .refCount (),
  • updateUser (), addComment () er på den anden side kun engangsoperationer og i dette tilfælde vil share () gøre det bedre,
  • passering af klikbegivenhed, der er indpakket i Observable - RxView.clicks (view) - bør også have en operator (), for at være sikker på, at klikbegivenheden udsendes til hver abonnent.

TL; DR

  • share () -> deler det observerbare til alle abonnenter, udsender ikke den nyeste værdi til nye abonnenter
  • replay (1) .refCount () -> deler det observerbare til alle abonnenter og udsender den nyeste værdi til hver nye abonnent

Hvis du kan lide mit arbejde, tryk på knappen ❤, og lad mig vide, hvad du synes i kommentarer.