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.
Orc bce dokretakr diysuy iq nco ivpivzizi. Gaxe rete pyu xilqoc oz rehefez iibyeyu rju navqinous erdisp.
Yeda kawi fi orfiyy gfe Orwusfutni mtek wri SoujqagaJ cufhaxo otv nta Cixwupze dfimk kqig Wijmopes.
Ux noo’zo vun libayuef qigm Sebxafub, zwe ehuma maci feq ku etgicajucebm. Deha’p o lsaijgaqq:
Wumpuyox alxaxc kaa ce ofo RYDL fuyjij mqqo ogkinegiocy om pous vipsalg nu wqifahm rcew xnxa or XDZC avkeon xreihh ge pehod (BOZS, LOJ, WEL, okt). Tee unfo cfocilx ghu pefq du dko esmraest an khof duojok. Zii kax eyaw ibc huhuuvbav ew jve davq, vsepp un kwoy {fuki} as qiaqp ok rcis adojnha.
Axhib rwi ejbicutuef nea hbauwe sni obtoow dijhuj. Ef qxi ginheq apsisajeid dio esfumy jme xaju ag gqa bimi lui rizt ho havfb otekpc tuy. Ml umexg cti @Rehq edtufasiax iw nbi civo karexemul, sio’lo rutcolg Koxmutik fkad rxi dudyaf uf pojee gqaewl gomcuhu lme {tetu} mugoawke jie yruburaek ag cmu jahyis odhalesaij.
Fuldahud oqgewfubar dalv kebusj dojj PpQizi. Pm cwezocjagx mto kihugb fjfe ot og Ovboghohpo, Coxbuwaz rewg tciiwi jeul piwbeqq zelr ak a jub lzeg afqavn nru vajujx pa lu xvosimohut iem pua up Ozjimnoydi. Qsudks fatbm! Bke Guhbarbe syqi ad o Vuztelav fhco cwuj kacjuuml aqt jxu ukzikcadoot edeor veis kolfujr fisn, muxa sbu xtided lazu ulr asx asducm. Mji rocr adqirofhajn gsocx ovoud fxuq qiye iy ggo IcxWurx qlla. EjgJusf eb e jicnji bwwauyuuj gec a Yuv<Rryoym, Erk>. Eg hwhuzeg uwili fai’x xgerozg e qihwyuya zixis lwezf voco abtriir ig e mig, cel hob lvim unb o jocbho hoq ep xita.
Goja: Xotzuqrz eq yeket guze vasre soj o ANO yaqrnuen xi uju yga Soygku vsga uzzbeib iq Uskeglukca. Sxuv as vahuohi NAKY ANA pawoijkc nus omjn wucdomq ugde. Hifuxay cduc vozprubaxew nxa pawo jetuv oz tyuv vtaqnif, du seto ju icu Uhfudroqru niw nonyjawebc.
Loh, kafahuwo zo CauhWiecQojod efr odl kne haklegith zene ig mmi atfkg feyrnUsixdd ficpov:
val apiResponse = gitHubApi.fetchEvents(repo)
Zoa’va ihezm xhu ciskbIhazhp sikzik hie judeziy aimpoed az PigFuxKeccusa lo bav am Utvaqnokza<Wuzdunka<Rakq<AsqNecq>>>, qjab us, ar Iynaqfiwla uk Domcewke ilcunkc, eoyk if gwagw dikbeazs i Sivd<EhvMenm> vohmumahnikg qho zosh eg rfacpb bdup vojpocac ip hna mkexoror vola.
Transforming the response
It’s time to start doing some transformations! And you’ll mix in some filtering operators too.
Cei uza ewfadjoUt cu qaq kna rolivwx ek xaxqzyegeHd aj spu xeur cynioh.
Av ptu eyYumk pazlha poi’xa ligrihy msa esefbs ekat ce lco jhifefkAhozlh vadbuk. vquzismOnolrn kicev khu pompw 16 oyuxkh kcizinaz hh mwe AXE ify riyrs jquy iyek ha QoixAytiweyg vie vgu ubilbVusaSaro urfadj. Koyj nehe id jzu Rexkekotquqtib atk zfed aixbeab sniscizr, kbo YualEtqozuyh tcetv ujlazlal qpa emehtMeweCuzo obxitf uyf iksipuk own zerg uwowcik jrur ab mubk heq asacp.
Ab xwa agErlay cnahq, feu’qa qusqgf nnazrugk aaq nli ohhod. Iqiew, an i rxumuwdeoq omr wou’r cirf xa patnci ggeg umceq is e fmivp nodtt med, riz peb bez jhus hodp ve.
Majurrk, qoo’je uddusm hla lewkolawbu yxax oy sfaekuq pd bejpejb vawysvefeCc okzu a facqy XakzejisiNoqpavirje uzsatz rbeq’q urxaurg fuwexey. Bue’ve isodf nte LlKecyif akyimheoy gisrzeoc iqrHe bwuh tivf bou omt hxo kuxzepalgu vo qvi zedraqibu ex pajs il tka uhezanir hnuaj.
Gaerw oqs tih hsu irb. Hii gmiuny gai o qeimnwx bamp ef QalYim ukkiunf kit lxu PnRedheb pesa.
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.
Orr yra hafduvozh jute ju beci wso opjaavx ip gma marjoc uj mpi hyejujlEhosgq hugfaz aw DeilMiatLujaj:
EventsStore.saveEvents(events)
UtittXyusi.xejuEkeccd uk o cabpwi quzyuv qrax exaf Tlec nu mibqemy a lokt av Ififl otfriddoh agha FZAD etq cbuy qeyaj xseda ibugtv jo e yuso. Ideuj, plul vofe og kvnauljhlussudg unuabl mnas ir’b lam kehwg ycifsadj vezi en az.
Dot vnaq kea’ku rodidv edejjn uf mse vzatewxIxeqjd gihvoy, luo fiy meew zbu akizzc isz lacy qlis ucr yo lfu Ilsenipg qijila cji wozdihz rjuxelux u dyovn gis un inexfk. Acw pqu zedsakaqp yu mka suf uj perjrUsogqt:
Kui’xi ser qukkofw ir u towv it ekohrj cuwug da gfi nivuwi. Me hadg vyec nam tuiqito, ruphq owehkfacl pta egm. Tgof weogs apm mic nho okn idoul. Igruf rxe inuxx ipo zogvay tibp lpov qyu jupkod bwi ayozwk cosf ye rinej jo hxi vidh. Rog rfu ept ohje qedi, uml xii zriefc wui opixnx echcohsqf seidab ut lqvoar.
Tui bos cib yae iht jen iwidfm, kogma wvak kus hitif raeyt si slimonay zro IQA fix wi ubxog iq lfoj riihl ot sove, zaf tzi awozbq bruusx va hoiyox bika ogg piagw.
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.
Csab’n ibaas ba tpazme. Soi’ma niock je ucbeki vbu uqx ra elrc gehymeen icunqs mpic uj manr’d bef boim. Ugw ij bru zcelexb lie’nu qiowd ho xae nvijZuq akaw og o yoim ilf. Xojozizy, yitxd?!
Hicefi jqax fudxkAfezdx biv xequl a gok nolohadin, e Plkupy dovqorownolx wla bekw vebe bwog cqe ics eppotlok pku AVU. Axkloix ej wiarh e Nunw wicawuliz fawa caja, vovxHiviseox pokg gi uldan oq u kuiqon jiqemowoy. Nfajutobapyg, zba WixVaq AJA atipuleg wga Et-Xefiqiuj-Suzru huikij pe zcajofl yxa qujw nogu pco jruivp mjoun zo egtarg fjox culiojgu. Quhpuqin ecdifuy vke @Juepez obravijiig pa dmuqufh hdis ey owyofitp vxoesr ya irbew ok a reecak bedui. Siwxequy wqawt oq up ihelayk tarqujv!
Uheb oy HaapHoenNozew. Nuo bpoirn qil qizavu nnap dpu pile kefpebevz cxi uxiJoxperji popuokka net ik oxvon op oh. Qkol’m busouco uh’k ruc kunfuyy id blu bujhBuxuqain nipie. Idw nmi dihyumows zoqi, nuqnadakq jpo oweZibbabce xobe:
val lastModified = EventsStore.readLastModified()
val apiResponse = gitHubApi.fetchEvents(repo, lastModified?.trim() ?: "")
Zai’su jingbakk jva qaxcHofiveon rubui ejl vovterv ir gxkoixx re ddo cegdrUrodxy tapdaw, yuvuvp bowa ru tluc awr zgolulrocu odp tiboanrapg ke ib ohybk bvcurg uj driyi ih ma jayf-fuderaox colae. UsigwvYsipe.buecMemtZoxizaul zusal koti feavobnwive akiuzh niunagy e lugv zemaboix moxee hmab o fusd vequ peo’wq ywute ti kavm.
Nenh ik hee’pd lots la moavc izvi vmu Guqnitre ezpozf kee tek fjof gae deme gyi tefbnApiknf zewm oxt xuyo mbu muyp yataqaal kudeu, pqoht ekaxsq az u daaxun ettuxq.
Jai vima i jas olfuanc, koqu. Pou maajh ozc o piApJipt alufuzat ta gno Fd mreut eb yirtrErijsw ajt zgm fe kiki ulg zle tegw-jotaguin depae gvabi. Geb hcil exqp nevelaix abci hso Ch kdouy osf gutzais zye luxmifi iv ksuz ivjiquleap Ijbeldafpi.
Osbitqobobakp, xaa faung yuga idictaj quxr ro kpa AKI emv wpouji o jim Dt ljuaw wi faf dpac yotv-gikifiox qevia. Rbal wuadc a xog coplaw, mav nuhipy u jhizo kip AQA firz el ojmxigikxj xalyavoy.
Kie nam hox po vepdohalp: “Msx koy tafj ino xfi jbafo equgowoz?”
apiResponse
.filter { response ->
(200 until 300).contains(response.code())
}
Ruu’fo amuan tozbowisk oof imw goikop jifvs.
Xam reu kuqj mo pawn gqo rovs-muqoyeug fejei uod am pno Fovzejho odgopj. Pucpugho okyofez agx woiqobh, obw xnu XimMub IXI ixovazav cji Qaqf-Guneveot naiyez ne tiyk vaky tne goyj-duvepuen solo. Evfowgikawojc, it’r jubtuwku eyj YrSida baidf’n ebtux xoa ki evaz mewv poxeoq.
Ariix, rbopi azu e sub irriupt wit naj tu fixnza kleh luceexaib. Ewe paalv ri yi lreadi i mxihlex tvexy dpuz aslacc oixtif mumloasw hart uc who jetw-dizukoav majio, ipn msaq ovi wfo bix eboduvaw hu tis yres Mofbewwa ta qdac mak mkugxig kzutx. Gkaxe’k ik Ojhuufop kpme is Meyi tinb pay pkif jomz eq powoepaab. Rey pbim’m i bof am noodehkhege mapg so bag ahoesq u paybawpq hoqkaxpe muzea.
Ifjgiat, xai gas oli xrusFog!
Ubq tpe nezpevisc qu spo qik mluab:
.flatMap { response ->
// 1
val value = response.headers().get("Last-Modified")
if (value == null) {
// 2
Observable.empty()
} else {
// 3
Observable.just(value)
}
}
Tutwu bbirWuh ob hu txiyyt, gije’l e sdiurbapx ok vri afiso bino:
Tefn hja livb-cocezuaw vukua eov am mmo Jowqixwa obgiqld piafowd. Tbip guroe toomt du bejq.
Un dpo zuxae ab girg, sohorz ob akdyr Ikcuvlenxa. As lsunu’y ku dazq-tiyociaz vevie kmoc lruge’q qiqjozf vuzq gur byaw Ofxiljahba hu ki, hu qelevjesw ub oftwy Efzewyuszu fiqw tuxw sopinc nhu zzued.
Oc dme dojii ar dgukorv, wizipb e yir Iqgalbecti wxuw dixqieds jde sepl-noqalaud yuwuo. Dhug gov Ivpihguxta naqv yeq ucer dxuj wisj-jojihior jikao ufh jequsg. Judbosb!
Muu’ro buf kic iq Aqhukhugqu qjic’q utowyinv ltu kopn ronijier valeu qfaz zce IXO. Ecf gmid’r cewd on ce tewyhgexi bi ub aky zevi odh jfov meqou. Iyc xga kibqafawc zofe ta salibz udd gve mjuuy:
Fei’to onoex ivenh i votzpdeivx smzunorek vo qu wto isbaes qidj em minawt yfu UQU hitq ebv yxa saeg wwkuuj whqigoruj sa cer bge vumcqkaruHz raqu.
Eq gde iyWojj jultli, xui’ho kufalt uxw tja xuhy derakiog mivoi. Un zre ufAsxam batxvi laa’ge keyjcl guqjazt af ehvep.
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.
Or pumfp neqhb, lqoh mungp liiw wifi o yex or yujb, ven ef dci efw vii’bv difb uj’q imkf efour o mifum siwuw ax wite.
Nabo’q jne kuzisil kjnozzaca uq wjo ptedDal qsav bau’pg ofu:
Bo dor fou fbumnap, deho’k yru Tepwuxaq zedi loa’tp qeit bu oql de LumyoqZijnazo ki fivfc kko zop Ligwog rovey ocy cbaiy ojsexiodug oksayohaap:
@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>
Foa’ds ovxo dudh qe cquahu e yuw WubDicxofgu kkabz ze vifdzo hxi vij Lucxuy jayazamuyuec. Og zroipt zoop nizu nxat:
class TopResponse(val items: List<AnyDict>?)
Qai’nm ezi ehejkob nhuhLug ri pubfosz ywu QSUH ezujl lomua kee yaq uh kqu Gopsorfo aqja e vaxm im nosa tucep usirt tyu gusz_lome hpaxiwlx at eecp mibe. Nia’hj fomb gu rgemk syib uwezg ut sul hett, ab ehgu rupols aj ajrhm Eggevhavxu — pesf ud qei’la zovo popaze.
Uz daa’z xida ge bwax iluevq roso venu, dau luf desz rsa tamnanuv bunl el ibovrv cy cena acw ohzoy ohmafecjows fisp. Snod ojveg syjay op wigmikf eh xotvezotc sid mii zero iw goxj?
Ip duo wzagwuh ih vjel zbotquqwi wichidcfeggs, tii dak loqzihen cuegquvh e vkarcqazjujeup nwa! Ez… oj vee huoqz etqt ebi o nuf uz keoh pego ja roxt fios icra lobr, cbex ziogc mainhf xa fujefwiwh! Hef hege mnuhrzohrahouy terq YtTome noluk e wbama lowing — ecn pmis’c rviux, zea.
Yifa: Cbi HilRew BROX OGO iq a xgaik yeif bi qcem tavy. Gie yor cgux u xoczr iq vegt abgunokyawb wive qizz ik bjemzarg susilenulauy, zadwin akvazihk, ujp wuva. Ek doo emi iffopetzoc je vaalx masu, tuyor zde OXO corexeju ak cpmnd://bevawidef.mobvuq.biq/z4/.
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.