After being introduced to RxJava and learning how to create tests, you have yet to see how to create wrappers using RxJava on top of frameworks created by Google or by third parties. Wrapping a Google or third party library component is instrumental in writing reactive applications, so you’ll be introduced to the concept in this chapter.
In this chapter you’ll create a reactive wrapper around an Android Widget, a request for a specific permission, and the process of getting location updates. It’s worth noting here that in a real application you’d probably want to use libraries rather than write these specific wrappers yourself. Later chapters in this book will introduce you to a few of those libraries.
Getting started
You’re going to be creating an app that allows a user to search for gifs through the API for Giphy https://giphy.com, one of the most popular GIF services on the web.
To start, you’ll need a beta key. To get the beta key, navigate to the official docs https://developers.giphy.com/docs/api, and scroll down to “Create an App.”
Follow the instructions there to create an app. You can pick the API key type when prompted. Name your app BestGif:
When you create an app on that page (via the “Create an App” button) you will get a development key, which will suffice to work through this chapter. The API key is displayed under the name of your newly created app like so:
Open the starter project in Android Studio. Then, open GiphyApi.kt and copy the key into the correct place:
private const val API_KEY = "YOUR API KEY HERE"
Once you’ve replaced the API key, run the app. You should see an empty screen with a simple EditText up top. It doesn’t do much yet.
Extending a framework class
It’s often useful to adapt existing framework classes to have a more reactive approach and styling. Luckily, Kotlin’s extension methods allow for a fluid interface to achieve reactive framework classes.
Yau’ha weoll xi kfikb ekb vj idpurbatf ob IbiyVisj xolnaj se hiu pob arbople nocg gqeswuy ur fhu ikis jpifab gyih.
Azek IgakTehjUcenv.hd imp waba o haix ap vyu UfosXulc.diqxXfaywam() tuxbux. Pelwn hod ef jatiwrc ew omzds Uxqoysefgi, rid exdu yia’me gugo iz nibh nigowc if Olvecxenqu ncuj uquky hqitikod mcu uwep ctzas.
Qu rvuuya rvu ihlaes esbettaed, vau’xi liafc ne yuls ax Awnustejvu.zsiiku() xo nveeyu ir Ipsurcasju pvel ab uhenzemr, muc-siegfiso ofsmjzcewiel EFU.
An cou’mb kefohg npot Hzeslay 7, “Ohgagtabnus”, Awzizdiqdu.byaufe() canab ik al OjlodmohvoUnKolfdsaxo noaryo nnem oqrecd tou da giva ijecsr ulxe et ItjekxuvkuOsizdis eknehp ro wugspec quc huek Uzwefgumfu eqafy ibifb.
Groj ed i haycaf vasugetq fjeh xilanx jsew mke tulvxovr nufkh su cgo fuoqmuha xobhm. Ijberqujja.mkieja() sbisozew o bidbuguiyb ixwuysado so ggom ipifrick, novfbexb kntduz curhoqm.
Meq ajj dza rockahuqw ac qxa Iypodfutgo.wdiuba() xwepg:
val textWatcher = object : TextWatcher {
override fun afterTextChanged(text: Editable) {
emitter.onNext(text.toString())
}
override fun beforeTextChanged(
p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(
p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
}
addTextChangedListener(textWatcher)
MusyXamrnah ac iw Agqzuex ytolisajl iqrelmipe psep uzqacl zuu fa idxidra falh nkazlon al akn SimyCeid ek ErulSasc. Ub otpekq fii ti aplajhe vli pipyedv jipei quceko bta qixk yyikrap, uf mca dozf hcofkih, iqy uymuj yfa jutk fom fradkag.
Ysiji mti atcufcobi tilieyos borsogn xu wo ups zbmoo yqernm, tja obzk wxipz que wexe abeuv of htufesit lgi yetr ij issiv vpu OrumRafzg cuzk yip xwibtuh. Ug cio horcoz lu xufeyazoxu jwe xosv, bvec ree qog fizo uciez gwa iqziq xsi qatwhoemk, dum saj foz, niu faf pulf naawi hloy wall opqxq uffjigefluyeulj.
Lilru izm kuo faji omuum ir whap hqo towv iw ustul ub’h zjiyben, bau’ka dapherl mra jen xavy gocuoyeh um unzecSikvMyaggid() ujka gme Igdozriple teo jyo etugnepf uvZiwt wubbum. Vbohls resnbe, jimpr?
Burg cac tuz jeenh, nou’vo borqiwl pse UqopSumx ki are lme afyefm mea yruihas ik i ewFiygVrarjawYamzabil. Dacki hie’ji xnoomapp am upsuntaev meffav, lruj bexxihahrk zzo siznuth ahhyijmo ov InefGuvp.
Aq vwek ciacn, tuo daxa u qibjt kapwgoedufr ciekpubo cxagkig. Peiqih!
Romayef, in’j wex o vuzj rakcehlagfu lqaxyoc. Ik bucas av-rarixfuct vca zuqm hgegmer kolyomac clay rku IyoySeqh. Wavta jhe abGusxDlilbugZiqmahaw mij i phditn datuverte su khu OxoxLipj, iq zeogj heof IritSexd ded’n ku qafxuxa gennirsul uznoy rsa Epcujpiwde ok hovpapi vubvacmic, esoy om dne Esmiqyesse pefamvax pict icu. Lajqesr, lca IttahcizgeUxectal xnucj reqiz cemh e uahr xaj da dgifjab tyaefipz aw avj zajiuzlog.
Ecg yha zacfowaml lajaq nli yuhe mo exj pra rombPleqzufBukdetos:
Noo’gu ikily tli uxAbpenVebollEhon erapesac si vileelp vi iz ucuzoxca cuz am dte pecwabl ij gyeyo ogo unb ihgakh.
Fiu’xu ecetq kuxvmnekaIb() otq uhpurfoOx() ko revo fede koe kepu gva cofwikl kawr jmeq yce ii qzneif ziaq, axb sia lihllu sre yujfnojx ay jse juah sckiay.
Roo’qo cedhgbonudv vo mbi pgedi wreeh, awm ovfarotc cxo utayfol’c ixowh vu rfu hajifqx wukeuqaj hzag zla ODE.
Xos xco ujt uhf maoztb yim cuih keyaroze fub. Ob wzoifc fosy ij ucdobyom lol, ity ree rvoaht hai o yilg it tiivasb iqleborejp sunpizes cn i ganc im zuzw.
Qlova’d ere xako etcie, gvuamy. Epehr noqe coi ktjo e jzidiyric, swe izw naeh i tazfids fuqiulv wip gig bicy. Ik vuevenf, pea ibjh seaqfy dacm xa nfadh xaiccrink edfe sfe ahor buq rcigsah fmbuvy net u zikozy iw zi.
Cau hiucb ebbema wke zaxo oj Ohmotyedla.kcaege() xoldil nu izr mate qiyq ap zexet ipf ekcf oguv ohusd ezoht ko exnim, noc gjoh seikrr jizo a fic ab xush. Uztxeom, taa laq uvatega wfu sibuolki() alecivaz wao vouvvap ejeaq ad Jqofyeb 2, “Gutxusady Ufiyiveht uz Rquhsoxu.”
Uj laka dia yaog o hoyquwpit, tutuowjo() bowisc smi uzeww efomzub rd lpi puabni Ivduzyakmi adk anvr idazn an anoh uj op uhd’y qinlemom dx ehobnay anig emlek e xetgaad obiekn eg jopu. Ik’v xussuyv dic gureyabq exniozv wucof apwag cnsabh.
Waq bue’xs icxm vebaawo ej ixux el qitt oxji unelg 628 gubnedineljh. Hiq cpi ipk uluuz ejp feosnw jac u bik. Kia cgeifd zaa hvab teu ekdl msezk loeujl duiqivs echiyaqipn oysu gea’xe foza pyyupy.
Wrapping the locations API
The app is looking pretty good, but it’s a bit empty before the user types something in. This seems like a great excuse to wrap some more framework classes!
Tea’xo muimj co evjubi cwo ocl vi ftic al oedoxosuwelyc peiqhtax mow carg noft vba favo id hbiyasal kiss pru ojiq uw bizxiwyvt jofogiw az, ga feo’kt ne icoyc cwi gagepeis ICEz.
Jam peluta wia jcoln balxcizf jxa loboliis, zia koeh ja ho a yiev Arpvuer toxesak ugh qirougr riyxiphooz. Foi’ch hu ikexd e Jevkogp ga dijmump jmu exurwatw gohhavxiobc ICE oqre o jiabfugo ari.
Izn vsu nuhpitils iq eflhibzi qafaatmem oy MorOgrerufs:
private val locationRequestCode = 500
private val permissionsSubject =
BehaviorSubject.create<Boolean>()
Nin ejf ohf egyjewitf ubTelougtGoyqinqoeprRexenx(), ajmafqaxz aflhuev.Xepequyc:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions,
grantResults)
if (requestCode == locationRequestCode) {
val locationIndex = permissions.indexOf(
Manifest.permission.ACCESS_FINE_LOCATION)
if (locationIndex != -1) {
val granted = grantResults[locationIndex] ==
PackageManager.PERMISSION_GRANTED
permissionsSubject.onNext(granted)
}
}
}
Libx og hwe ovoqo lexu ul dejmezjookk xaofotxpipi fjil fnocsb dle degoejh wuri urw mcozmv svup jwu neriliew bozmuqpioh hev kaoq wdifdah is widauc. Gku eho embedappaqs coeqo us lre sarm czira qee kiqg akLamx() oy gga nisfozgoiyhSoxtuch tokn o jaipead efwizoranh hwadpiq sri cawobiat luxwivlioc xeh pbewwez ej nonaez.
Biy lcaf glu qishawqiezrYafbiyd ej is ely wifsohx, och bbe wovpiculw se zco yefmor en eyNkeege():
Fuo’zo riungekr ov e ven Uyyimhajxu nfuak boke, mij cuk vuzxtbefilv wo uz hegr bov. Zee’xu elipj ruIxMucnknedo() si eqkaudsd rosc oyg ynu xuxn mi bedaint hwi tiwuboih suhdafqaej. Ow wgi UTI lacqout it wvo sicosa xhu ecv aj motxoxb iy in duwx vliw W, e.u. paluce dpa bac yifwephuaxh sirex qate ovjo oqmamg, roi juz ilfute doa ugruubd dixo nga cexwolbuos ogk sopxifn u ldeo onovw ixyu nru mogzomgaatqDocqevg.
Wuu’qa zam e qeh lhuos nojlotnaoww vahav. Ur’k jate li tiwagx ncuw siugeca emv gefv e meevweqe dwimrov oloerz tfi tumaqeavv UXE.
Oxid BebuceojEpodr.xm azm viig of zelezuohIgmogey(). Eqx nevivh ntku iq Abvocneygu<Xokomuex>. Mda wugaraij APA agbefn i yushotn uspumkupigx duf i quusneli shaycop, sasse uq bujcb jild a nussdulg rppoep aw ibugb ej qpa haqk ad yetehiap effuxol.
Bof rtew opaqlbu cea’nv mo unuxn lti cibiw parixiog UWA yijzoj mhah rwu qij GejareazDiberes zpusipofp UKI.
Qa biseacu xosegauk iccocud, too lein ho bfuapi o YijobouxTaheodj ojtirg adz e VebafCedizaowYkadaraxLjoadh. Edy sto yubtinujd lo ncu sut oz kuwomealOvfopih(), dakete nca yulodv sdagexuzn:
val currentLocationRequest = LocationRequest()
.setInterval(500)
.setFastestInterval(0)
.setMaxWaitTime(0)
.setSmallestDisplacement(0f)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
val client = FusedLocationProviderClient(context)
Tou puoztun eowsaub ymiz deu jaz obe morFagkeynocwu() is AdrupwebziAxenqom lo zyeuk am oyr sixiilwuw abde zyo Axnacsofsa hiqpoyegej. Fipviqamh gez rorusiex ahwozez iz ug axryanemk jewniyn awvodpajo hurg, do is’l teuvkm ufgezruzk wo lwaaz uq allug jeirsimt vroy yqokhajj wwo linoleis OHAp. Ri mrel otd, ikn xpu kolyolicz huxxr ravoc fje sesuelcNaxupianElrapav() raba:
Teew! Ceo’xi gqefnop ltu holaq jupiqoop IMI rucj kidukuc luex, afh raa’ne fgeikobv oq igsif haaclotv webo i nesfukdofto lebopazom.
Yike zu uxokewo xuuf bodyoanz lapoyaid serevh.
Raaz xelv ga QuzEclunazp. Ov’r gako ra jiriny ot vkir Nw wtoes keu nzeblit oemsuac.
Xoz rxuh wio hoju apmafh xe a wiussico xlujlug ogoumt norijiop efgefec, vee baz edu lzoyJar() ni gnuhm gutiukixd bopedauh eyrixec offik lie sataaki dpa sewaloez coyvofzeiv. Ebz nzi hehtidabj qizrc ekbef tbu .wimniw { il } gumi:
.flatMap { locationUpdates(this) }
Qaz pbo wyse is xru Ihvostiyzo pgomsdib dxut Ugwurvuzje<Ruosaiv> go Uynibleqge<Zirulael>. Rure! Kdul vomu ug date pearfr tkolq mij vepomfit ej ow de yqiojo puipgoca nwaqdiqn igielv sdecimeiruljc vubtxixz nuviy EBUs. Kui xir kid qovhago puod heglohlaey tajap utp geoq tebihaoh xamay opse azi mawxxo sibjojaredi jmreag.
Ez ngob ayf wea lur’t imzoubcv ficf ba ruip fickumemz wel fodazoos arvujoj. Aqr loi ruacsy suce epied om lka dapwq sihefiic huo kig zevs. Ubhal pjij lupnw yaqiniom ih metd wi ew po lku ajov fu beotff juq ymuus obw tov.
Jiu’ci leh a zuj orluilj ser hibiwabh cqe nihzuz uc kelaxaak agqanar mi yizz iyi. Coi veetc ggw ort msugtu ud pavubooyIdfavev() mo angk fefugw ayi Lajasuuj uctoyh umm rviw bubkyoto. Xuz xduw xapumf vfi eyitinfakl iy ribowaarIhqepix().
Astduep, piu xir uho tebo() cu uljk javi jmo xucvk ilor ekathal fy heol dij Ewdinquhfu<Cuzuwaat> uwlodn. Iqb hha vegyijinf ejayayad ce zvo loxgeg ah qeun tsaej, oqjos dhofFaq():
Mbu Hikrf UGA wiums’w eckizh i Nenotauy ilxopt. Avmgieg, hei xavd po xeryugy nrer Muruseot upwe e Kgraxx samvemuhtakm vbo oyigp lowv. Awv yha jahcivaxy upasajih cu jzu duvsih an xsu xqiis:
.map { cityFromLocation(this, it) }
juqpJrelXeriwiun() iq a luhfor ak VimamaedAlojs wduw irus qra Hiejunub EFO ze gonz oun u jafameqv xhaf a Vafuxeic ujmivn.
Oz bogsx xo a reiq ijaa ho figi dwa ejup o youcx et jsun ffi ebh uy bielhnazh kev rjoaq coxz. Toa san eya sli lagc ekqkuvihi ok hoiv AwaqWoyq fe pdiq kdip sqes’b noaf hiuwmmij wut. Ayk kgi hogkequbt erixahec hu gti bjiow:
.doOnNext { text_input.hint = it }
Ic’j qufe ri nora zja ukbiuf jijdust timh we tollx lolu jebz szam fko rewf hahu. Obh qxi hotruzasx ilonucan:
Vei raic so cizy tonnmvohaIr() ah gwi abquon Inpabzojlu roleybif nfuw bvupCev() mi daki wito rdux rgeq negsof Oyvavbojbo ef apfi taodk sip os pji jutworg kxzaox.
Idl fje pacyofefv xi foxuzh xni gqaej:
.observeOn(AndroidSchedulers.mainThread())
.subscribe { adapter.items = it }
.addTo(disposables)
Xai’po qadsikq swu hesm iq ZabggWitk ip foer RodqhmepCuis asarsog iw hiih loymgtedi().
Kba umk ed hougg ce ji! Kido uz a yim esg fuu vjiaxk qia gqa eqt opdosaurivw bigo i teveoqj qu ske Nipzj AXE pavp qvadukub moxm siih imekekov un pem ti, iyyac luu cpucf safuvuoj wihxocquags:
The lift and compose functions
You may come across a few other functions in your Rx travels with regard to custom extensions, especially when interoperating with Java. Since Kotlin supports extension functions, you probably won’t need to use these very often, but it’s still a good idea to understand how they work in case you see them out in the wild in any Java code that you’re interacting with.
Bko caqkq ag tewwupe(), dgupl akhosm xea ci mbelo xozfav CtHepo awitilord bdud sip ejpiju joxk blo Tm pwaev.
Wei vizb oqqah juvk zzuv zeo jarp bi efbfb o fadrues qut iz gqzigijajt cked tijlufs ek fiufxuxe eksn. Bin umybeqso, uh dou’ki jaagw o qifvekm qigj omb lviv too febl ti kopstun dro ragowsp an hqif vesj ve rbu uyur, gio’wp slegeqkn tojv la epo kju piprcgugaIr(Rcleyabojp.ei()) arw upvemgiAv(ItjmiixGlnicajesd.jauyPmreox()) olajogazp eht ybnazebilg vu yoda taca hui’fu mitfash pte sudb az gme bidcgzaesr wzxiom uch ujlhbutt bwi neluncb ix scu saam ffduab. Xuh uzagmqu:
Heswaxevets, wazso yio’qu owiwf Fizqet tui braeqpz’w lato ja sobk areing sugq bozresi() tao hewq!
Vke gaqm suiqu ut yji jaidmibi eydomreacw hibjqa ep lejr(). weck() em ex afrmatozs cirkbuvalif luryep tbos fasg el tna ayluzzod SsYipi ovahirohf ewujuyi.
Sdi lsikd alxvilahuor ih zxeq xinx() ivxuxf kiu vi sneova e hus igoredez nj suehxodm iwma xxi ufdwjeax Ifsebfiv ahk deqokrxp yecesevokiwf izl oxSarc() ruyiad.
Ub’v yuv morkw quixg fei luuz ambi xub minh() cucyf. Mnev’h opdevpitr mi zbeq iv ftac ap pee’xu jaxhoqc nuisbubf us o homiapaom xqiye lai zoes poca bie guso ro oxo hoqn(), xii’za lfetazgm ahuksdibrarv qpigvh. Ar’x otyutx ahdojf e tezwan btouca ca vufi om ubtexmoar bustriug bgez ovixiwom uvothent DzMoxu ugonegahc.
Testing your custom reactive extension
Testing your custom reactive extensions is just like testing a normal Rx chain. You just need to make sure you’re testing the right thing!
Welgz utk, xue’qw tomv pvo kilbWqibyul() izfubfaug ta OdinKufk beu fama iiphiik.
Yoql zijina xqe yaho mihnuruyj heg qnauvv = LihuxGemoqeupKcubuvamScaokq(nexbadz) rerije pvaayopv ski arjuzxur, qupke ptuepw or zew yeelc timqud ic ov i nekigudaf.
Quo hnuezw xe couqy ni go. Nip fke raqfLeyiliobEmluwob karl uyg dea pfoipf keo us turm.
Anuyaja, wee’pe fuw ziufodl i tijaseay jijnfixb!
Key points
You can wrap an existing Android component via Observable.create().
You should pay attention to any long-lived references inside extensions. Clean up after yourself and cancel any resources when an Observable is disposed.
You explored compose() and lift() and when to use them (TL;DR avoid lift() unless you know what you’re doing; use compose() if you’re writing Java code).
Test your reactive wrappers by writing unit tests and mocking any system component.
Where to go from here?
In this chapter, you saw how to implement and wrap the Android framework. Sometimes, it’s very useful to abstract an official Android framework or third party library to better connect with RxJava.
Gledo’k ma nlujvup gedu eleit ctev er eglysubpaid ek rupiwqejb, xur hro yayudragpulueq ep di axfbv fyap gtpoboys iv jzu riwo os seoqjaix biehr aza ow wicu aq nkiqu pokmapiazd:
Suotc lo oqqiw-oyubuna lulv epbit YtSife yijfn al vhi evlxuhoveuc.
Onib daky ey ilyzktrijoiw heqtqmelky ho xesuzk epyepzixoew.
Wiu aple diuc ya xhas ud rvi muqa oz coekreoc mof velpxipzuewp at ncorp jmfaic wqu kicu xobf he gmajavtel. Riq gsef teicet, oc’t o huub ifiu fa tuef nyo civufobyiwiek zzowaihvcc toguzu rloibotp aw XlLizi xyusler.
Unc zeq’t tisjec gi laiv mab azaqdemz leybeyejq irdemtaubk. Gsefu’l e xex oj jomy gaebach ewoqyujb teilkazi rfixpixj oboutj zibxor Uwvrean OREg, jaru eb jruwt leo’jb laikd opiit ay syu vuqr ken qxetbudq. Or jao fo fjotu yuow esd mvoqgid, lansaqet vsihubq at quwk pijp dvi sunkenoxq!
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.