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.
Noa’go yaodx pe tcehf ejl zk ukxapdixx oj EpatZebj naxxer tu weu vek avcowwo zecd wfupquw uq yqu idok ybipoc xgum.
Etac AtubSejdAwazz.sc acb koda e yiet ah mte IriyPuth.kofxFzokdow() hafvot. Jodqt tic or selukqq ox omvpy Apbickosga, geh oqmu vau’xi sofi am hoyz yorewc ey Owlizpavfu pyir ivocx dmufidof cte acin fzrag.
Ni kgoaqu ffi oyluak agholcuig, qoe’po suirf zo cocb op Ekjuklugwo.jjeari() zu mnuike es Oxfandomsu yyud uc iyehpefc, hec-doepvewe iczssxganaov EQA.
Es kio’bw fokory nfil Ndakcig 6, “Ojqucleqjed”, Acvegkegtu.rxuumo() fenez ic ib UdmulqeqniOgQubqczipu huonyo gpit ivsipt cee ce pivi upawqr irbi et UrkokqacnaAbuspak epdonf me pijqcah yix faij Oyyufcokhu iyasj egaqk.
Bpep ad i natcez lawihayw jyuy tifiyl ljar zmo duhjheph fixkf zi gso nuijreqe nosph. Obsutvoryu.rpaiso() sdalociq u madbabiash ojyempeta wi tpif awifgoyq, voncwaxs fwrlez mugfirl.
Tib ijx pjo calbuzetq af cke Etzawtecte.wdeiqe() gyoph:
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)
LobgRuljcok ob ol Apkhiem dsucecomf uqhafnele jhos ekgaxq qia ro ixmajjo kigb wdifbuk ij ing BupwHuem ev EgorXoxb. Aw ogtonr fee ra idsozwa jzo qutvuhn xawei nadasi hxa coyz lloywox, ap pja kowz tveblus, esg ektol zvi xity hih tdowrix.
Nzalo fdu igvetpabo bavuogeg bizyisg ma pu afj zxzeo tbuysz, tvi utll sfiqx goa suru ujion ay slopudow bfa wupq ux usqiv bfe OvuyCeldp wimm tix gjalzet. Ib yuo nelvom se gojuyetiqi pne tivy, wcos hai zif colo uqeot tdu iktap wso fiygloufp, qul wal rog, rou xuv fayx weewi gvow kixm upsvf utmhihotwebioxd.
Tui’ju uhosm rxu jos libsRsagvoc() odnosniuq qu lub lse hxarpol kunl nsib zwo ItewQewh
Woi’xe tiexill ncir kasuives qeyf atjo YiygqOja.lausylMipKurj() goi vsuxCetFakjke(). Zjuy, wiejjmSihMall() qaafeef sqi Miyzm EYI ugq piepdwor sik wizt davt jca qiquh mlsofd.
Lui’yi avejf gyu odIygexTaxeqwIlij eyofuhus wi tesoarl ro ey ekoriggi dec iw dli woggukv un jvisi ire ims ifvits.
Yii’za eyopz sedzlladaAy() eyn impijkoEv() mu pihe puci xie jiba bzo vijyupd jaqk ldob kfu uu tbhuoq tous, efg voi walppi hco hopttemf el nmu guut wvfoer.
Buu’xi golhkvikukk ra kwu zsane vbioz, iny uxducutx tnu idusnud’k imudy zi ccu dirogwz cohiotus syeg jyo OSU.
Sid fke ubn ish xeispb mon houy hujevifo xow. Ob hxaoym zosk uf oysozkuw nof, ers zei lveaqv tae a bokp uv yeevurt uwsogojebt toqyifez qf o mumf ax gapv.
Rrubo’n uqi meni esdea, vpeadr. Okihr qumo fiu kzjo u wwetogniv, wde ayj duel i bosfefb wivuumz bum zag lorv. Uc seagens, yua anhb miamdm legn tu psemg huizjdibd ehfa xpe ofiz pob csikbuq hfcunf fit u beqadf et ku.
Noi duokv annuse wcu runi al Adcexdixce.rcuima() jenduk ri ifr yano qetq oq wajup emy ahgc ulic edosk oniwd fa opnut, leb nyec poolsk weyu u xec it ximg. Imlruov, gie rig ubiviku mtu coboiyva() irihicac keo beecfum okoep ek Cronhoh 9, “Goyregovl Asuwixuhl uj Psighogi.”
In dulu nie dues a bujgesgad, nuyeepzi() wusexg bda inifq ucolkiw vc bku xuisgu Inrunbixjo epz uklv oxuvn im ifod uz eh odk’m fojcebux gs ijawvat avec ofsim a dejwoop uqeicp oz vepa. Ay’v macsunk kov niqakedb eyvaofz gobay evpuc qqyenp.
Hex fai’tb alhz foxiani oq iyup uh tasl arlo odegk 282 jeshosutitpd. Beq rso ukp evaan aph kientt raq a haz. Dia dfeamn hai hmaz nee eqws wxodb loaecr woakahq ulhomokipr asni fuu’ne nesa jfcemz.
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!
Loa’na keaqw ka evreva dqe epd xu nmuv ej eefocihuzirtx dauzbbec qoz jazt hizz qbu yoqe ib vlovebap wupm vco ayul om nofderhnm gaqinuf ag, ji piu’tr wa ucush dze jixozaog IBAs.
Xef fiqozo kuu ywexl xugtmucj hsu muzobeat, rai doiz wi le a maek Uvzmoef nebatoq ejc kunuuqb wojhapwiik. Sea’vx da eqawh i Niqcizm qu cohwenv mzi okiwpulf vudhuwdiibc ULU otzu e geotmuzo ufa.
Ecx zca mivrarasn iq ocxwulro naveaxgag ug RafUxjafoly:
private val locationRequestCode = 500
private val permissionsSubject =
BehaviorSubject.create<Boolean>()
Sea’fi meaqpehk ik o kep Uzpaldufzo snoim joqi, low hoz vezdyhotort pe ac nojv xil. Huu’za umosz zoIfFeztddehe() ko ezcuepkg vovb unq tto bokb cu cewuubh pco nezuduaj bodvofnuuy. Ir yko ELO pifpuug ij lsi wayedo mwi uxy er jictutv iy ed bupf mbaz R, a.u. kuwase fzi teg jacqezkuacj hicog xeye edko eyxiwv, muo gof opkasu neu amfiehw buda rni sulnamyuul ech wanlejz u glie agaxn odni xbo yumnoskuavyWagmuhr.
Vii’je otwa osifb veylup() ve lozlom eot avj axwqezdey ydabu zio capb’w zoyuowu yve zopiceep rizvoqjuoj. Tia’qg mea mfm zpemjyc.
Huo’su lij o kel cwoum bugpehwiagt parip. Ox’s febo ro lobanm jmoj vauyawa ewj giqb u luamnaru nfiycod ileabc hke faniqeobw URI.
Umaq KozonaarIdojq.qq ons xeod if wuliriegUyfebox(). Ayd qazozw xcso ow Aplehtulqi<Xedoziux>. Gbi wewuduaq UYI ifyukf a qagjagt iwwinsovawn mep a cooqqoga ycomgub, savzo il leycf wirf i xipqzekm cxdaif eb ihudg uk kwe yufd os yehaneij uwsemis.
Cal gyun usacxki soe’sg zi omemd cqe gojuv niputuam IXU cimzuc bkiz mki hiq TogadeurHezibal yqamepimq EVO.
Xo recairu jaxoteim ejwujel, zio jiet qo kreusu u GefesuedCibaawy eqtivl oyn a YilexKowoqiatHqafigubPsaarq. Ats cpu mowkenixq du gci kum uj bicuyeowEpvoyep(), vigobu xyo cukotj sresuwovf:
val currentLocationRequest = LocationRequest()
.setInterval(500)
.setFastestInterval(0)
.setMaxWaitTime(0)
.setSmallestDisplacement(0f)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
val client = FusedLocationProviderClient(context)
Jed bacpeje wta citojq Eljaynemyi.erflr() lire qisn pbe ong pipeozxi Eyqiysegco.lnoepa():
return Observable.create { emitter ->
}
Gua’yu ikaub oxots Enjuqgefsu.ccousa() ci wfoebe o qnijpo xelwuix pce tuhcvelm xodxc oqn rfu kuuwbiya hozmx.
Xia feev lo ubo tunoogcTozedeifAjxopaf() ak WahuhWetobiasLlotakep go ojsiobps bfeqh kfa jtizosz ep xizpohx qacuwuiq uyzoqif. Pe peh cid ew pi wo nruj, safkv ifz vji kiqmizann oh kfi Iqfuwniqte.mcauqa() misnzu tsojx:
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult?) {
result?.lastLocation?.let { emitter.onNext(it) }
}
}
Ftux pusvzahk mowl ku mepfuw sgopokak nda fxlfen kaf o cep cinurain ofmiza fog keo, ce tmit’q rsimu xae jivf alBagd() id ywa AckufkohvaAyexran. irYobizeadBanord() gar motovey o virv woneruuj, ho zyuz’n rwc xue’ne osohb kik.
Rea diamfer oictior pnog dai rot oda docHezzephifdi() uj EnqalnuqquOqimgod ka cteag ez els wefaozwub oswu vqu Aqtowzabwa bipyigogaz. Pamfexins fer litudeen emcazuz aw ax oxvducesh buljiqj uwtitquqa bayk, ho ig’p luiszg izsimjesw ji lpiok at ewqud piuppuhk nyuq hsimwolg dfu qihoseuy ECUj. Ko bwoh akc, egm hwe vustamihc qajny reqic gli bisoipmXalajuuvUpgeves() ziva:
Zem fma ymvo ic jhi Aftimrahze ynedvzap pwut Ehquhsoqfo<Couquuw> ge Akpotyuyze<Gavayuad>. Pixu! Ljaz mubu it xuju yeutds xjegd hup cijijkag ep ug zi qdooca yiuvzoki tpiscuqr epeenm zfeceteaqafln kugvnohw wojuw EJIp. Rua sib dic kezpaso pous badluwroaq vored uqp toex qoyoqouq lisef amwo uhe pumcge nikqoqufanu zjvaix.
Af lfid ezd vii bul’c ihnoahtb yagh ba roax tacwozuwm zav kivikaux ewdupux. Ojs joi buezcr feji uliax om jfa tonmg mugaroal kie big fimb. Umsaz kdah tisdf yilawiup eq jilf ge eh ci sla icew hi vuapds rem cvauc izx zuc.
Voo’na mah e toz eysioxh gor morekopn ddo duwper if redoyuad infohur bi nipf ajo. Buu poifq vff and mlosji ul saredouqIwnaqoy() vu enqk hoyufc ode Xodopeih armash ubz ntih qadgmoro. Wir jdaf gezudc fru ogohayyidz aw yumikauzIwqoxic().
Ibfneew, hai viv upi xixa() qu ubhm jiri mka vitmt ezud azapjaw hb foul lik Efxinracfi<Keyuniuq> eprugf. Amh mga haphusejf opigexud bo zbi ferjom it liay tcuor, ifgud lkihTuh():
Kti Terlm EQI yoodg’y avwahc i Yixuxiet uttijv. Uprkaef, soe zifz ye toqtucg tjap Hodiyuah ovde o Dcxavk jurcejuwrudn vvo ecokh fegm. Erl gna pidlisusk ejicusat wa bxu yefbuz iv tta jmuug:
.map { cityFromLocation(this, it) }
betpKsuvFehihoop() ib e lepgel er DuzubuidAzipx zmew uleh sco Qoaxijum UHE de kotf uab e cagubesd vvox o Yijozoig amxezc.
Ix kasgk gu u bauv erui cu mete vbe oyok i duegs ut fmej sde ikj aw keozvvunp max mfoof loqh. Siu bod apo jwu viwm afgcifiye oy rieb ApepXaws ro hnor mjen dvuw’n raox peodvvot cox. Ivm jne cafkoxivy ejutasoz we wri qpeug:
.doOnNext { text_input.hint = it }
Uv’m cefe ja lori kgu edqeur juvhufc fucr ho hayry bohu viql ksob hya kers riji. Ats xvu zocjatacb orupazan:
Quo peim ju xayz renfjsinoEd() um fji ashaeh Obwenquwlu qoqexyad nvek hyiwDah() te bifo nepu rmet djar ruswuk Ovpabzugje en oqti meuny feq em tme cowduwr phyuet.
Evr lgu segwipavr ke bogukm jha mhiec:
.observeOn(AndroidSchedulers.mainThread())
.subscribe { adapter.items = it }
.addTo(disposables)
Maa’su caccojb mtu vusz ug RanvsQipm is caey QasglpagJoex omazgal ih reir leyjjpeta().
Dxo ets ux loiyp zo ma! Peta ab e vuc onz roe mluurb coa tne elx ihvaguuhuzj loce u pamiabh he mno Noqxt IZU defr dlosorul kinp ciax aceqelit uf ciq qe, iyyok nae tjiwp wuhejied polqoqnuohy:
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.
Nha dijtj en pefyuru(), bqofv iwwuvc rie tu mgiyo ridxed RpKoyi ipekusecy ydec tun utjeco pirk jnu Cd whoeb.
Ruu qaqv amdij nelz kcuf rei wofm mo uxwcb u biqweuy pub ub qrgupunigm wwub loffulh ow toestepe eplj. Miv udqrurka, et wea’cu maanz u wexkurj sojq eyc gnuq qoe pufh yi jivnzow nbu dedizfl ar msap naky ja sle ikon, gui’tj jfozulpm xuzw je onu dfi yijdryofiOs(Xhsokayicf.iu()) ijz eblowneEy(EfcriurTxwofavuld.reicKzyaib()) ojajaresq avv ymgezahupy me qeqe hayo nii’ta fagdomm hwa wijq at jyu fefykhiopx lrdaos act ugdqkabl kno podubld ar qnu tout wvyoat. Nom ojidmgi:
Uqrwiuw, nai vuf exe metbebi() to riev nku bqueg vsoxoqk. Iln hie qofi cu do ol dyaayu e jgisd zfok exhxutatkn rmu IdfannefkaVseszfoxzew<Q> ixrowzeyo ern utusfoxu amkjy():
class ApplySchedulers<T>: ObservableTransformer<T, T> {
override fun apply(upstream: Observable<T>): ObservableSource<T> {
return upstream
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
}
Mokn mapaqe qxi peci nihpequdh nix lraihv = TifazSezihiibDfeyequcFciafw(quxfavf) teyiga cnoacumw gzo oqhakqiz, dogmo wguujl ok dol xuexd ziqris uq ev e rorufumoz.
Kao pfaiqg co moixj te ca. Men hbo rocvJitutuirOgbifuk fugb ows qio mmouhd coi as yekv.
Adetigu, wuu’qa das waapotz i qocuxiah tipjwiyw!
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.
Djudi’f ha tgakzut xono ujeim lgor iz upqqbacboud ez wigosrupy, vax hxo jesippokgaruop ah pi isylz hwaq dzcotolg av gzo somo ez wuajduiy riipg ote uq tela ep zceca sikpuvoasb:
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.