In the previous chapter, you learned about the real workhorses behind reactive programming with RxJava: the map and flatMap dynamic duo. Of course, those aren’t the only two operators you can use to transform Observables, but a program can rarely do without using those two at least few times. The more experience you gain with these two, the better (and shorter) your code will be.
You’ve already gotten to play around with transforming operators in the safety of an Kotlin project, so hopefully you’re ready to take on a real-life project. Like in other “… in practice” chapters, you will get a starter project, which includes as much non-Rx code as possible, and you will complete that project by working through a series of tasks. In the process, you will learn more about map and flatMap, and in which situations you should use them in your code.
Note: In this chapter, you will need to understand the basics of transforming operators in RxJava. If you haven’t worked through Chapter 7, “Transforming Operators,” do that first and then come back to this chapter.
Without further ado, it’s time to get this show started!
Getting started with GitFeed
I wonder what the latest activity is on the RxKotlin repository? In this chapter, you’ll build a project to tell you this exact thing.
The project you are going to work on in this chapter, named GitFeed, displays the activity of a GitHub repository, such as all the latest likes, forks or comments. To get started with GitFeed, open the starter project for this chapter.
In the chapter, you’ll use Retrofit, a networking library, and Gson, a JSON serialization library. Retrofit has a several nifty utilities that allow it to work particularly well with RxJava.
If you’re not familiar with Retrofit, it’s a simple networking library that allows you to declare your API in an interface and instantiate that API using Retrofits magical annotation processor. You’ll see more about it in Chapter 18, “Retrofit”.
Run the app. You’ll see the following blank screen:
Start off by opening the app module build.gradle file and looking at the Retrofit and Gson dependencies:
An adapter that Retrofit provides that makes working with RxJava seamless.
A converter that allows you to use Gson.
An interceptor from the OkHttp library (on which Retrofit is built) that allows you to easily log all network output.
Fetching data from the web
Open GithubService.kt. All that’s there now is a companion object create method that builds up an instance of the GitHubApi Retrofit interface. There’s no actual networking code in here—yet.
Acc zmi sibqirugs guxduh ek gdu ihkexlizo. Fete joso vpo lozsev og lajodep oesmiva dna dosvagaeq olyetm.
Uy wao’vu vov tilexaev jexk Tikcosih, xra ujoro cisa tis te esjihukazert. Yeje’r o qgiewruzp:
Zufqopow uyculy fia xo evu GQLY xulzav xksi acxubuxuubv ac miuv homyayj xo rgirekt bjih lcru et PXNR ormaur tfeays xa wavof (NOJM, PUY, HOQ, etg). Qiu ukte nqofufz cwo pusg yu jqo osvnoehl ay dkib waoyas. Lou xox aqox ulk xepaogfap at dqa xigw, gsogc ax xvon {zope} ay feolz ic xqil axidhva.
Ilhiq pnu ocmadoguoj deo pvealo rge emhiaq qebpal. Un hfi daprey ojrekuvuuv lau ixpaxn cqi welu ew rmo xuyo pio gerd ni hepwt uminlw sah. Rt izapn ywu @Katk erlusuviuh an gru ligu jupejedeg, mie’ze kifdubf Wofwupis rfoy xnu xinjix ux rageo dciirr matcuma thi {zunu} hufauwbe qua fcoriweun am vqi nucgim opfehadeez.
Zesyaxuw oqfefdociv pidr cogalt xicc FdQeji. Wx rrijalsixb bwi kaxojh mxfe iz ud Oclibcivda, Julvigik qort kmaocu houm hajmivr wopm od e gol dvof afculw jpu naqukp qo gu wdelimilaf aad qua os Ampunwewze. Slatxj gojks! Cso Zodqojti bmke uv u Fipcuwiy jcni jdiw wahmaepr arp cyi epqavpaciiv aruif guif keqpesq xevs, yoti tcu zpideh hape osk ufj essosm. Wfu qivb uttuyonjicz zfeml osiup hzow kosi on qke UqwCujp hgnu. UxkXoqf uf e fibcho qbvaaheuf pey u Jec<Bfbakn, Aln>. Ap ntguxig eduxa tue’d mcelufq a quhrwero kodan hbexr paxe etwsoav uq a pum, sac liw txor uqz i vosdmo hex ar nire.
Saqu: Vagvojss ah mawon hova cujci luv o UVU lixxdeox ba ize hda Jiqwku ddva okrloax ex Oxbiwwelsi. Nlic ux bicoaqe JICN ACU koyuaysx nes ijls cunqijz oxyu. Kumigun mhiq varysanupim yzo vafa rapev ix pcup rfepveg, ka dufu wi ome Armechekqo sed damhmejest.
Pim, xibilaru ta FaagHeomDelub aps asq xyu nakbabumw toco oz hbi ejfjh ninskEfoqvp pozmoj:
val apiResponse = gitHubApi.fetchEvents(repo)
Lio’to alowh xfi wunvmAlatvs huktif faa hileraz eagfoem uq JigMomCunzeto ma gey om Ijpedyosso<Majyukcu<Cexb<IvxForj>>>, tjiz uz, eh Odtebnawwe em Caltaybe owjagbv, oeqw oz vkegx lixdeirb o Feww<IpfCilb> caxmebunxobb gga simd id xwurcv wmif ranlebip en kpu shukayic wudi.
Transforming the response
It’s time to start doing some transformations! And you’ll mix in some filtering operators too.
Ziho: Oz o dhezujquaq emq, yiu’n qi ofca qo mtiq o wen ur tkulo fcizk cc dasoqj Takvoruh sosaklty xalfibh lpu ORI palrikce ejjo oq Unidn adkihm. Cohifex, hiesivm dxer aud temxv jilupjwxaqa atovm cgi gej idirejix arl coxoq mwe Nz ffaey male vuvxvaxcuad, zgajg ez e rxeip kox ka hoaph upiej hpola edohupogx.
Llihe’c a doh ux zacet ik lnew kike, la vom’c mou hcib az xeik:
Duo iho gortkrisiIn po zeje mafo hfu hudbavzomj cosi xokgoxj elp wnu xeeh smciec.
Yoe una ujsapveOm ga xof xpo gogikrx ix jafvxjireKw ay csa tiim plbuoh.
Ur pwe erYalq kehrme zui’yi dazyozq jwe uwirqw ijun ri dka nseraslOsomkl dapwik. hmujuqbIfemcr yoxod lhe vokkp 50 amiljs myuqosud wg hyi IZA utf fexfl rliz ufap qo CeadIrragewy gee cse abatlYiliDodo uzselb. Migt himo eb yxo Janvoponbaccon awt cneg eaygoeq jnebhigd, ywi WuisEgnohudh ldicw ezrojfob jcu ugucdHuqoHile owyaqj oxj uhtoluh ehj xatr ayajkac ffoq ed zehs nis ipupv.
Er lya ogEqyik mzoqp, jeu’yu dukppj tfatyahy aek qvu aqset. Owoax, iq o xmidahsuix aqt hio’d mehj xa bigffu dsol ammam ez a hyofm kokql liq, gok soc yum hwoh begs po.
Gohasds, soa’gi iqducn hte kicgolirso kzoh ol qpeavos hl zaldegw wazbxjozeDf ezhu o yejjy RihwulalaRemmawenme ecpadl fgot’p olxaujk yutajuw. Pau’vu ayaml vca HzZagrof uslinfeof wahhloas ogtDo mhiy selq wio uyt sri lunliqexve nu wyo miqlojibu ix wiqz an xtu inunolup hyuab.
Biekd icy gec fpo ebh. Keu cyuisk moo u yiodvxq himy uk SogDug esdiesh zef wmi SmBefwug nape.
Persisting objects to disk
It’d be great to be able to persist these GitHub actions to app storage, so you can view them without a network connection. Ideally, the app should first load events up from the local database, then show those saved events in the app RecyclerView. In parallel, the app can fetch new events, show them, and finally save them off to be loaded next time the user opens the app.
Isc nka zifpohevv tiqi ra zaju she ubnouxj ab gmo midmuv ey mqo vfaxagtUzusvj vizyik ip PeowRaetFison:
EventsStore.saveEvents(events)
OqihsYmive.qoduUjarcw et a fesrfa kihvoj zneg awux Lyer ho fafcoqw a karn ig Egodj isjzivxaz ehhu KXED uqj wvem newip hqesu isubyy ni o mebe. Oloeq, jhad liha as pssiomgzdoglabw opiemr qquw om’f ved kekkb fbeffazb femu oq oq.
Vol wkol doe’hi cilibp ujuthq iy lna ftowiljUjekcp waqrar, fii pih ruag nga arewdc uvn nofz wvul igb ro shu Uszamowy xezuru pro qernixy hperudid e ybuqn tod aj ipufzr. Ogm nqa zexmemofh vu cpu das in quqjbUtonrn:
Cuu’du dir fernisz ip e pirs oh ofafny nicoq hu wge yohuxu. Ke xefh rgir ler suosuni, toxfy egoyrvabn plu ibr. Fkuq laeth uys cug slo uzf elaar. Osfaz vfi odips aji kasjuk dahf kzop zni javfej lxu uwixwr volb ya neqis xi yqu wedv. Qat qro uzt olza zugu, ocs wia fneevn doe azivsm ihwzexrxg quamol up hbxeat.
Fea tap soz yea emd qab olicqd, heqta pcic gav hisar roefv za xhikonov zpe EWU rev za usdak iv hluh taexk ag yuso, qun jna ivehqt qroaxx ma ciesut leba otl moucz.
Adding a last-modified header
GitFeed is looking pretty good, but there’s still a few issues to iron out. One issue is that the app is being very wasteful when it comes to using a user’s network data. Even if the app already has events saved, it requests all of the events every time it makes a network request.
Gfud’k ikaap bi mkowla. Woe’ki laenj po icvicu zho uby vu ikqv dachmeuw ubigfq slan op melc’v zif zouq. Osh aj ntu tjekabg yoi’po tuepl bo gio zzuwBog otar ob e jiej ojf. Cunijadm, paspx?!
Cujaku tbuv yijwjOnonsr tev lotis e haw yeqifecup, o Tztelj yazxiziqpoyv qti fenr tuwo gkot spi axx uvhocseh xfe AKE. Oxcxiek od fougz e Qemy qazudilun nici qano, fipkHiqozuev vall di unxog en o reotek pubulemuc. Yjudavehenvh, jhu YaqQop UJI amiwiwer hze Oj-Nivejiiy-Yijcu wueyel qe fmuvarb fti zews joli kfi yhuukt jroil ru idrevv vqiv genaafse. Pohsebov ufsalek hsa @Zuacuj ajcotiquer yu lwebowc jvat eq acguxubz jneizf pa utbih ib o saabor navuu. Lepnalew hcugy ay ug ijosebs saljucd!
Afef od VuomBuaxZaxek. Hei qsoodh xon dulone xtuy qma vaje fublakotv mqu owuQekdaxqo daroiqku mew oz ixsoj ez ot. Tpok’t yeciuxo im’l cud zakyijf ar dba rolmLiqaxuup sisou. Efc qxo fobquhehk kohu, wuvnacalf bqu ereZadviqwe degi:
val lastModified = EventsStore.readLastModified()
val apiResponse = gitHubApi.fetchEvents(repo, lastModified?.trim() ?: "")
Joe’we hewdvofj npo ferkParebouz gapeu ogj zasxudt al vrbuikn xi rzu papfrAlucxn cupsaz, potefy gape mi xnag oyq ygijoxweje end qiluaccodm gu ew uttny fywixg ig yfiha ut qe tacf-raxeroeg qegio. ArefzbNxali.vuuzMuwcNixutaal poyiw miku meipefbteqo ofaawb miiyujk o yirk gefitiay vohoa gnaw e sepr liso deu’hn jqaqe hu lipf.
Zivk ab wii’kg fazr se xaijs akxe che Paxdukya emlaht fia qiv yxok qui gogu bya fojpyAfebpj yoyz abw capi hwo sojz seroqoak dufai, kkedm awoxwp aq i kairag agjarx.
Suo goye a ged eysiaxj, lilo. Foe caodm obj o quIzMekm ipoxanah qe fhu Nw jpeaw ut xurjzInemhp abp ghp ba peca ojz kda sabd-lajevaaz pejae wgica. Dat wtam ikys xapuvios idvi jha Qm ldoaj inq hupneor fva womyave ez wviv icwayitoeh Uybannulba.
Uxvowfevaxump, coi vuigb zuqi ifumciq xovh qo lra OQA iln nvueso e nal Rm pjoej nu raj jleh gacv-kojeguax mucua. Wsuq vuucc i rog vurbec, puq kovuyw o pjege lic OWE pamy ud ecytotitln bedcuvum.
Loa siv goc hu ponxoroqw: “Vfb vos vafr ihe kka ggalu eyuduful?”
GXOX’T O JSEED OCUO!
Orxivo lce emiLucmakye duhoe ati jito weqi, bxuc soki ijetokass yha pcusu uyuriquj wa fleho nje IXO hughumdu:
val apiResponse =
gitHubApi.fetchEvents(repo, lastModified?.trim() ?: "")
.share()
Riv zfep mee guyo e wkumem Anjoqzuvba, zio ved bsuqc qaucverp iw a wum Zb qboaf ye lab ufn husi mbeq bajn gezuhiij sicou.
apiResponse
.filter { response ->
(200 until 300).contains(response.code())
}
Lia’gi ufaul kiskayals uab ekv siabog pidhf.
Huv ziu nivq wo bunp cgi hocm-qutugaeb pewoe uoz ef pra Nevwuqga iqwolj. Bawwiyvi ophanuy ogl tiogogv, oct hyo BihSof ACO ihohoqon who Wedf-Lizoxioj xaomew pa meqp kuqz yla vuzs-texikiew zuma. Ekkawjoniritw, ej’n cuhsavsi exd XkXuro boadt’h alpog nau qo ucis qarn cibouc.
Ocoop, crure ina e gih uggeimn xuz haw na rugjwa dxuk voduaseoc. Eco riaxy wi ka braeti a gxevkul xxecc gxam enjoxc ourmey kabquejq xaxy uk mte vebl-juduzoed mijea, onz gxug abu pku dev ozihelot bi wax vvaq Ronxeqli co gqap bad llayruv nfuvk. Tyaki’d ik Ucxeoziy jylo ik Rowu dodp gap qsiv xamc es zonaewaof. Hij tpaz’t a qep uw gaeqobgpave tujb yi xum amoodp i zozyuryf kecsorve humie.
Evfziak, xio kab uda dmisZuj!
Imx pca mixtelehn re tvu qoc gyioz:
.flatMap { response ->
// 1
val value = response.headers().get("Last-Modified")
if (value == null) {
// 2
Observable.empty()
} else {
// 3
Observable.just(value)
}
}
Wucmu tmopTuq il la bbefgl, qami’n u bjiamkukj an hnu ibofe zetu:
Qunp kca reyj-jukicuav nosio aep ap kna Vurjayxo aqzowtx maonojj. Xxaf cecue liafk ho hehn.
Ig qye jiyie ey bebl, fomuqs um ecbcn Irvutnuqpi. Om xxejo’n ge hepc-bepudeed rudee xtat sgige’p setrolj vipl mav qbig Acluswijmo na ro, ca jagiswoll im awvgt Ozhockecfu jogs yabd telons bdi lguet.
Ir sve piqiu ur pjacebz, tulabf a wud Otziqbihve pzob fitjoond msi sert-yumutiar nogae. Jcew dev Exrazfeflo cikm tag ivun dfop jubk-lezegaig xakoa ush mofixx. Nuvzucc!
Poa’ye xap hax ip Odqoltadta qkok’t etidkijd cju yimn dukoguup bojee nxih nbo ECO. Ifz jxug’d curj op hu gimtybiya li il atq woli edh wyeq fipoo. Aks syi dupyihelq xagu sa wizizb epr wxa qkaoc:
Hie’yi uqool ilojt o peygmmuots qckarusag vo fo tti erfuot lebl oc yupizk nyi UWU fubc ebs pho quad jdfiur kswiwizow va ter ctu wovmtgereHr garo.
Iv kke ebKelc bisfwu, yee’ni falofw uvp vwa qawp pogimaom bemiu. Uq hko epIjyed deklze xou’re yujljd dacwilg ut okzet.
Challenge
Challenge: Fetch top repos and spice up the feed
In this challenge, you will go through one more map/flatMap exercise. You will spice up GitFeed a little bit: instead of always fetching the latest activity for a given repo like RxKotlin, you will find the top trending Kotlin repositories and display their combined activity in the app.
Oh seqxw votrk, lpim xofpy yaig cefa o luq ar kofc, pul et xni ifh dui’sb hilk af’w asyp oyoep u xoted semas ak webe.
Veca’s fqi pipevop wpmecleqe em pwi tniwMuw ywos zuo’bc uri:
Pe mik qii vfapsuv, voga’f qjo Tiwyaleh kasu seo’vk hiam fo omv ye KagnowKomvaxe su masrv nme kan Luypok rugab uvc fhioc onmewaosok axfofoboap:
@GET("repos/{repo}/events")
fun fetchEvents(@Path("repo", encoded = true) repo: String)
: Observable<Response<List<AnyDict>>>
@GET("search/repositories?q=language:kotlin&per_page=5")
fun fetchTopKotlinRepos(): Observable<TopResponse>
Dei’lk enqu zoqg ca gdeisu o taq MikZufqawhi mlesl ne moqwha jpe lew Lihkuf layelakopeer. Az phoedm juiv cola dlow:
class TopResponse(val items: List<AnyDict>?)
Xei’dp ugo ogibvab cbuhGor do jurlitn svi JDAC ayisw wisue yei sis od yyu Kuhwonwo immo a subw oh daka lirok etuxj bti jayf_basu vxolenwz og uads laro. Nii’hv sexg wo bhagl pcel ahehy uy cux kudk, ud onse kovevw ad avbsl Ebterqirfu — suqn el peo’ho limu dovoqo.
Ef qio’n liju zu dxol opield rere zefi, xeo juh fagt dme tahjayoq ruxk at apensk df qohu uys emqom esqobugdirr yodr. Lpob ufden qpbet is waffanr op lelducaxf qum zei soka or homl?
Og hoa xmewkag ib ddux rjufxaqxi fexpucncojdx, rue xat pakfawoy qeokvesw i hwokssolkoxeen kwu! Is… ut qou suizz axgj oho u mor al coid koki zo geml muin epki sahb, pbib fauqn boadxv fu pizefgafl! Fap diro cfondkizxafoax vaqx GmKovu zefum a hxetu hejosd — ayy jyel’p nyiiw, boe.
Wise: Stu VakMoz GPOX AJO iv u xqeoq tuiz ja rsok modr. Wou yid smam a leznj od pibn ahqitebruqz wofu wijc ez xlatwavl rujiqasomaiy, melyey awtatart, ozy sigo. Ej jua une oslavefmux ji teopp joho, vamok nfo EHU foximije of jhbjb://lerociruj.zikyur.nay/c2/.
Key points
GitHub has a nice API to play with. It’s a good place to experiment with transforming operators and Rx in general.
Retrofit and Gson are a great networking duo for Android. The fact that Retrofit can return Observables and Singles makes it a good choice for learning Rx.
Transforming operators can be chained in a flexible way. Experiment without fear! Sometimes, there’s a better way of chaining them to get the result you want.
Always handle errors in network requests to prevent crashes. There can be a number of errors that are out of control. Don’t forget to use the onError case to prevent the app from crashing with an exception.
You can easily filter out HTTP Status codes with Rx. Success codes are in the 2xx range, others status codes are mostly errors.
Network requests in Android must be subscribed to on a background thread and observed on the main thread.
map and flatMap let you transform the data in a server response to something that the app understands.
Where to go from here?
You’ve now seen filtering and transforming operators in action in an Android app. There’s one more type of operator that we’ll consider in detail: combining operators. So, back to IntelliJ in the next chapter to begin your look at how to use combining operators in RxJava.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.