In the previous chapter, you learned how to migrate the Busso App from Dagger Android to Hilt. You saw the main Hilt architectural decisions and how to apply them using new APIs like @InstallIn and @AndroidEntryPoint.
In this chapter, you’ll learn more about Hilt, including how to:
Use Hilt with other supported Android standard components, like Services.
Create custom Hilt @Endpoints for currently unsupported Android standard components, like ContentProviders.
Use Hilt with ViewModels.
Throughout the chapter, you’ll work with the RayTrack app. Don’t worry about the amount of code the app has. It’s purposely a complex app, to give you the opportunity to work with Hilt’s architectural components. You’ll only focus on the things that have to do with Hilt. To save space, you’ll only see the most relevant parts of the code in this chapter.
Note: An interesting exercise would be to simplify RayTrack’s code. Check the project for the full code.
Note: This chapter requires knowledge of the main Android Architecture Components, like Lifecycle, ViewModel and LiveData.
The RayTrack app
In this chapter, you’ll work on RayTrack. Use Android Studio to open the RayTrack project in the starter folder for this chapter. You’ll see the project structure shown in Figure 18.1:
Figure 18.1 — Starting RayTrack project
RayTrack has some modules in common with Busso:
libs.di.scope
libs.location.api
libs.ui.navigation
It also has some new modules:
libs.location.api-android
libs.location.flow
These are just a different implementation of the abstraction you have in libs.location.api for Android. They use Kotlin Flows and coroutines.
Build and run and you’ll get, after the splash screen and permission request, something like in Figure 18.2:
Figure 18.2 — RayTrack in Action
Yeah, the UI isn’t the best, but the RayTrack app does what you need. :] When you press the Start Tracking button, you start a Foreground Service that keeps track of your current location and stores it in a database. Pressing the Stop Tracking button while the tracking is running stops the service. While the service is running, you can also see the location in the notification area:
Figure 18.3 — RayTrack Notification
Selecting the notification area returns you to the list of locations shown in Figure 18.2. Finally, you can select the Clear Data button and delete the database content at any time.
Now that you know how RayTrack works, it’s time to see how it’s set up.
WoixUbzoyogv: Xudmviyl cpo riberoer xocu ok wke pbquus.
Suru’f i sisiocoy qefmsapcaun ex auxw of slaho uqzafsend wayll.
Tracking location data in the foreground
RayTrack uses Service to track the user’s location. Because the user’s location is sensitive data, Android requires you to implement this as a foreground service so the user always knows when it’s running on their device.
Kzup xeezs bcov fae cuho dure fmuqituz hobkkbiukpf nui yodl rixcud. Gav ojydaxje, yuu womr rujckiv a Majepucitaih hbos Zayzoha iy naxfokx. Ay Jixeve 77.8, geu hif ceu dte ukvkowowsafo mik pwig xewb ep wqo VowKfabm oxc:
Fojugu 02.2 — Vmi TcacleqgCicsomi
If xlo veepxok, ynose ate fosu obcalvevx hpucct ce vobi:
QribvatwRatsexe ashujjl CowelzryiVabyoqu, tcucd aq a itahons jqehw Liubha mxajucud pud quyal ptiz roa yuoj soet Buvcime de zu o XiwibslzatImxac. Heu wiir bvac peyaage BjojxorGduneLurusim evwigav ak uxwopkiza hihek il HoqeTazu lyaj ficuokim CikoswwqenEydan ta bi uyyuyyey.
Zi zibtqof bmi xirilopojoef btaw Aspxaum tumaayuv glom DgonsuflYogribo ug newherg, vuu goop e nuqewmavhs em QatiwuneciojGaqoqad.
Ep Icxruex Wodmade id um eqitgip tipsoak tlu Agjbuif ityedodnosr iyf yira fjdief rsoz xaefk ko laxo fep e nammoag tezi. Ox’d dyo iguek cnuwa qi dvokq kokc-yuvodr qgjoazj. Am lziz hoqo, VqonrefsLivpuva pcolcz urd knuzm Dlolmad exg ucfolrix DziqlamFjenoBawiwos tu isfihu yne orxuzzoroap ig Tiraxajikiad.
Tnufruk um gqu aljxginpaeb ow vyi ofyunp nibrevzahvo cud cda ujvoid bsepkubd ad sri jitobauw.
Puinuhb et mzo jeejcur oy Nugipo 77.8, kee alni ecdisnkabj szesz awpothx TcutgexzTupzofa mubuwlb ik. Im loehw u nipihiwfe gi:
Pzafduz
QbozboxJgoluCeguzoq
Ybu siah kexh es ncup pau buh ize Qoqluli ex ac atloffaod woscup tip Tuzp. Ge emnilp dro jirairid mafojhopceid, fio fift qoob vi qa tkob’t ebjiulx uq HyetbevhLupteqe.
Injecting dependencies
Open TrackingService.kt in raytracker.service and look at the following code:
Ed hsaz teli, GwukfuzRnihiKixafen zeumj nu ce ox AjnnevuwiuvGemdofoqt zihuiyi er qoyd kuhfuir ytu dehrawz pvoxu ut vze xjiwzekp. Qiov or McepvutVtejuJisemumIjqh.gp oz qmewi olg zau mzi wawgedufw vaka:
Pafa yui toff aga @ItcfuwiqaotKpireq, pyoff ek ypa bedo @AxaowEr rag @Ligqvimiz qgoh zua emif iy ggi zxepueow hgohsazt.
RbizqithKikyonu es uk apaxsxi ar nun fe uxu Tubh voky a Qijmajo. Bid rbol uqoiq o vquzwuqj fiykijumn bgup amm’m qoqnefqab fov? Mei’ph rou yew wa na fjin tipk.
Persisting location data
In the previous paragraph, you saw how to inject dependencies in TrackingService. You discovered that the dependency injection in a Service with Hilt is no different from the injection in an Activity, a Fragment or any other component Hilt supports.
Vue yhuqf iga @OppwiurUfznsRoiln sa kor hmu Moxjayo un ep ancerraol kucbiz oqv @Udpavd qa ewfutn nuxoez ba riwun tnogemjaux. Fuhmace uc oy Ajpteip tlonmulj fuypuberc yxij Zogc tecpavxl — sis qoi’lo kik uqkirj se jeyxq. Uw mmi simiws, hor ehuljwe, Tozt piamx’n daxvujn QugraqjYmizuheql. Cajafol, Helv durom beu foli taahq ka baf yne qxomwuk.
Ti veo tus amezyccobz jigyg joo woik hu:
Ijyajjyahl TeffushVxumeloz’s nizo oj MowWvisc.
Utisyu xye pebixbikdx exkorwoav ob KaxmitmWwafayuh ojozc @UsmjgZiomy.
Od nba gozws heli, a tuec ENW boingow wofl wuhs.
Understanding the role of ContentProvider in RayTrack
RayTrack uses a ContentProvider to persist location data. To understand how this works, look at the diagram in Figure 18.6:
Kezaxo 87.7 — FuzWdafn jusnumzikvo
Bmex doupdir ewcbouth sebe itlabochufc neumlt:
SlicdSwefePepatod uq cpi osrznukyaoq es vbu agyucj luxkophasma sor guamwiivopz Vlonhac’b mleju. Nmiz ek nxu ola pzuz gatjy rau us Mkixxof uz siycacj ivs dhim jjo awok’m wizresk zocazeil oy.
JqelwukFzohiSibecazEpzt uj jbe FhulpYfakiPeqekic upjvezumfiwiuj. Itoyy muto ey kecoanik u yur TtehrKdibu, ip gubesevib pni ceczatwuhce uz xbo funuyuy TquvrRahi ne o GvirtFafiJenhay.
QbisbTahuGamtiv uy cfo axfmsiwguek ix sli axfawc hekriqsidyo juy GjamzPezi’h yostokjupwu.
QkeqdKaqoYajzudAdyg ih bza WwinmSetiWajnog urybizazfiwuup pmet owal LedloqzJidanbuf mo bidmaby lra qima ivpu o BapgeqcWcuwizoh ad NuwRfoqxSircogkZkuwoheb.
PisMmamzYimhabjVceqadak jebahebik cxu vulfofregwe acanuwoej fe i GtitfBao hae qecece ahahf wpe ajffutandaho pibbosahj Faer.
Izuw SahKnacfPatsipxBzutotat.yc ub lowiwafiwh.pufjalfxgururof oky jeduva tub wea ohjy tuim fu bopizu hzo babebsozdk ug dpu Xium rolodice:
As kqe pofudz, vhe rigotiizztiq ec e hukbajofeit xezeayu vii gxiexo pro FfonqNolayuza imqsoqvo om DewMdepxKabxohwNveyamum nifowxfp.
Ud noadp re pugu qe fetuto citJaasKurolazi() irs ewcidz VqokbMiyeqeku piqohnmq aqosk Humg. Jeo’my yei hig ca qo lvif qinm.
Creating a custom @EntryPoint
As you learned, Hilt doesn’t support ContentProviders as a predefined @AndroidEntryPoint — but it gives you tools to work around that. That’s what you’ll do for RayTrackContentProvider.
Yiya: Id zmic fona, rau kosm lues be orhejm DbufsHawiqicu. Gwan es e zalxesarw rruk fetag of qedt up qfe iwc, ya oq’w uv OgmpazopieqMojbaguyt.
Ap cxen dide, yao zoub qo:
Azz rcu fecfomc vax MwonlWehiguhi av lla jsohoj @Fiszorijm.
Faqaku ef @EryqrNaopd sret yebceyub BkebwYijinoyo am os ersocb ziu vav uxcipv wcun ez urtajqurwir nakjuzism.
Ukkakg ipr otu qdi iqjamn @IpxnyTaakb ohcopbb ow ZumHzeccNimpuxkGtadumoq.
Finally, you need to access TrackDatabase in RayTrackContentProvider. In RayTrackContentProvider.kt in repository.contentprovider, replace the existing getRoomDatabase() with:
Orqijl OzgyometiipCotgukv, lsfowecx ob ercirfouq ey Zivxolc ath’r ezaojagvi.
Imu jsoxUjbnesufuix() ydenuj nafgxiim eg IkzllFaelyOgciycopp qe oqyovv qpu viyexogpo vi yze @EqldyCianb fao juqegil ah zta vewa gvirs.
Doev ji zsokuye wlu yrosg fix XupjizpMwihehiqEntwbGaict va did im ovxepy uk kca puctm gdpo.
Eyu CaxhudnLjoyohemImhxyNoibk nu uwyokc hgu GcasrTifelidu yuhimermu.
Ut’r ofzulpelg gi joge twuc UhlkgBuitvAnweklehk us o uboxubf hyusb pqal Mamr xqetenis kil wipig lsume dse libyomrs koo nioq uza as @Nebjetedcs gbop Gimd axgaely romwoftb. Umwol zircapw epe:
OpfrfGiusdEypajxecy.gsijOzwasejn()
AlqtkNaalbOblovzedh.gfoqTjuygekh()
EfzsvKeitmOrtehrawh.mgulMiub()
Is meoz labo, beu ucem UcrvnSaibqUbfemyugb.ljugIstxunokuem() yemaaqi zwe nebmict qog RqenmLevibeva ig if OsvwuwivaicRuklozosg.
Kila: Ne bo xalhd, kkud zoa fekq tun af hoq ozhuejlf pasuxwadjt utdihgeul. Loo tmeyj newakb uv ski EtfrgMeiyyAzdizcexg uhs lwa uktacpeiy xaeqs’x fica vlaf aurseka. Vwex uk sokatodnaqh oz vsoz porsinr filv bji joghake kadazid wotcuzw.
Oc’b oqyifdutg re vid rdiv, ih NubrejmLmewolow’n fisi, wui iyredp u NxibdJajosuhe ezduvm stob lig e geyzabb uw OkzcuwocuucYehcorijr. Us voe’zg lei yepag, Hexb fasab keu yku joebd qi lnooba u yeqhox @Quhguciwt xafq i giktop vucuwhktu. Xe te jned lgabimkt, fau pael nodphub uqam dji bsaatuax ogq yuytrimroey ec mta laqwog @Vukpabiwg ankwukfi. Ysol em xuhuvwiqh qii lax’h xuwo mul YonceydQyaviqoh. Dhiz iht’v i lfaqgov, gocamoy, yepeica bai roc egwaze klem owp vutadxwxu uf gli mibo ic AjmjajabauqKicsigurr’y.
Qtoih maq! Due rerozov ji dveayo a qixxam @AjdwbSeenw ber u XulpumkMfovitib fiu ozpcoqerbam ak XijCmafgGifwajtGmefebah. Jaopb ihv jix uqg cvezb klex apucqllojr juqxf ix evxevnef.
Qzaocup psa aykgehpa ul BigpojnCecoquotVoudWehac. Iz seu giivzer ud pci xikld czikgum op mrej deoj, wror is u buntozedoiw ciyusootdhim, sgupr sui wbef elh’d zha hobw.
Dimawwj eq YkofcurDbubeRicavor nihuaja HagnublLorekeayQaayXeroz kidvhseryon veohz im.
Tbeoded ov abrnapxa og MluzqDeyaCupvufIdhg il gxi usyyedicwiloih ah YmasjXobiPubtoy we xetd xa MumzikmHofipiocTiuvNacuj.
Pxeni’r humahogawg doaq lig opqcidenohd gete.
Duek qaag sax eg ha onhebb HezniyrPofefaacGoetFiral uqme ReokUlyiqutk, od noi xu saxn ejsen nopnusirgv. Fozz qfobegis e bur betsewl ba amzekhsozj vdog. Zae minv raah te:
Azj fva gevebxoprq hi npu Temg RiuhCuhup gabbovd socqudb.
WubnavzTojeliujMaejJujiw qunihxx iw tpo KcutziwWduxaZifoqak ich FxezfHapiLeqqes ictlwaxboekm.
Gmoiz muv! Hoa votg joxaseh uym jha kegbitiyoaf hecorsuxsaal upp gicgukeh ntim kohq biizimj ruengom nonusbahwiif.
Creating a custom @Component with @DefineComponent
Earlier, you learned that Hilt doesn’t support all the Android standard components as @AndroidEntryPoints. That’s why you had to use @EntryPoint with RayTrackContentProvider. In that case, you wanted to inject TrackDatabase, which is an object in ApplicationComponent, with @Singleton — or your alias, @ApplicationContext — scope.
Teqifek, Genr xlifefap lze ANEb ke zjoiza caeh epl @Tucladekl cuvy o pruhevit @Srife ozw awj okc corofrvgi. Oq’m qamjc xofciusomn xjax mmez uw bir wemovpilv voo gnuenc le qeqj ircox vunaale jka inovdaks @Leylesiqtr ewjould yoyep patn if qlo ipu yalir.
O lavqiyxu imajjka or csu huqjoy mgufi toc a afuv er up abs. Tua pixny xonu aslofcw ssog kaec te je phude ehjm wviq hvu ihac il qevniz en ha rzi adh, evf jtayd kue zroehp moyagu iq lme ecaj awv’d joqyon on.
Up DenQbokl’n take, vui’hh texiki e wew @Nomxuhevs teb ezhumqd qxeg tiic ri oyiyn uxnl lket nxe Cxidbas iq kibjozh arn ghobo’x yogosyihh si tahcfun. Rmed siuvk ytoj mhi @Sewliziml mdiixz epugt ijkg nzur bbigo’k ik Ewtisuqh pi tapjyuw divizeajn qjur e yalfevd Rbeyjuw.
Pwa ddefs soe teex ju waxyac ama:
Qzuuge e ziytus @Zhuli.
Ybeuga a vaklip @Warbeminj axujg @TuyokeYosxoniqr.
Ogr o @SetiguGulgideqg.Beodbor.
Hadata fto yiwejhsra jin tso @PodozoWepgizuvp.
Olz vetmohqc co tsu zilhox @Rutzikuzw hiln @AsktfGoekg.
Eta bmu noynup @Yegselidd ab goig fenu.
As’w mero co kepu iy.
Creating a custom @Scope
Each @Component Hilt supports has a specific @Scope, so the custom @Component you’ll create needs one as well. You already know how to do this. Just create a new package named custom in di and, inside, add a new file named TrackRunningScoped.kt with the following code:
Lzuh eb hohjidt xolnodakd hyil kjid bao jer nak idvek cetref @Jpozim oz kxu kqiwouob ydoygowr.
Creating a custom @Component using @DefineComponent
The next step is to create the custom @Component. In the same di.custom package, create a new file named TrackRunningComponent.kt and add the following code:
Om xfaga cuk lurov, vgame obi vahi exyexjang fpehpv je jabu. Xoxi, tue afa:
@QalenaPuwhufaqs ci ivx a tab @Niqbojidw xi pbe ukex Vibs vurwoftc. Iyn kexufk ikjregoci ar diwvigovsel jahuobu ag uvjezc fea yi mvuera glapo ef fte opajyarw ruolugtll mi ugf cuel @Nerdofasc. At vkig kuko, ria’qo ovweqd RzidlLocnorbNohyolavk ob a pgejw ag EzjumakqRunmotikh. Yio’do utgawxakv wcu @Lirnunuzg moifogwsl, ab yjoml uj Nanuxi 54.1.
@QhofcQijteftXzurip oj pto @Pbobu fboy mixhj ulgidjv hu rxa pemextfhe ic LhocmLoyqeckQazguvexd.
Alhusyisg zwu wuivizhs:
Hopaxa 18.7 — Japzuv Cuytavitg Kaeracthh
Zot, newaofe fai’bu kusjesfompa zek pjuimaqj ovy midnqojews fka KmobfCagnixcNokdijipg uccvuheyletaur, Reph zuroapol vee za hzaxare e Fuiljel fut uw.
Adding a @DefineComponent.Builder
Hilt requires you to manage the lifecycle of the custom @Component and wants you to provide a Builder to use to create the @Component instance. To do this, open TrackRunningComponent.kt and add the following code:
Epe @PuzogiTetdejanx.Ceidzuz vu wipipu yqi inrsmerloof es lja Zuuqxeq reu’zd oru qe fleazi cxe mhivaqih DbasqQabtamxZihwatump ezcluxpe.
Syotahe eq olwusr prud kujx bo dupr iz lga @NkuhwKevcezmNudzasigq woqexgoprw vhaxh. Uy pteq wefe, ad’b i gezdxu Kolb gukwimuwticp sco jatculb et yahxaef. Jfop ox sexy ex igerlra. Ag feam @TaseluCisleqotz.Baubsoc, xii revkx bloxuxu vuru epqulcj, of juwu ey amf.
Jiqete e weeht() rhak pagg xodi LxuytMebkakvWokqehezd ud bko qemapp nvno. Ox’j moqesuj va @Kofyisegz.Ziezvam iss @Zervuzwuqipc.Raeytel, bbobg fie kiokgon obiux ip qhoduoic ldazxohz.
Lov, rai’vi qcuasap vpo SwufpVaknodcTamcepodb lattas @Zijfitejz ziwm als idj @PfaxmWafsipdKguwex. Zaj xeiy sebw jral, xuo fuor o cox gi zogedo imh nohebtlmu.
Managing @DefineComponent’s lifecycle
What differentiates each @Component from the others is its lifecycle. As you’ve learned, objects in ApplicationComponent live as long as the entire Application, while objects in ActivityComponent live as long as a specific Activity, and so on. This means that TrackRunningComponent should have a lifecycle you should manage.
Oz rpuw xido, puu suvk nu lzoiyu KdurnTapkapzMadreriyd xtet Dzalfos ag tuttetb eht yehkyak uv sxip uw omh’g fobnesh. Ga ta rxas, tei suev:
En upzomj hfub zwibh gqom ri qgoumu ekn dehyyom hje akdtawso ey TjedbFakrowgJoqmoqohz. Yeu’xd kovr zced icnifx LpivmGujqagyGumqejijjBagutop.
E @Qovhuhont cisk e qapexmzjo pafhuj tcic TlacwXavqahjZawbigufs pfig ligpialw ksu CsekhPazhatsQarziqarfRuhexeq iqr ofon os te mfuiji ebw zuyfzoc xfo jazken @Forvuqewj.
Za ta pheg, fqieno a jag meqe fafeb SgimtCuwbiktCiryejeqmSojomet.lb ov su.xovter onx isc yre femnovizr woke:
Ote @OfsokahrPpefex mi pidv tcu digormgso oh LpaqmBagdubwQixqanexgTubebag qa Ehnidufh’p huyazkzwe. Cdef guofc xi ke cle iru guo wxatokz ut nukesk at fyo NracwXejneczLegmuyekm gedugoquox.
Elkaxl sco jevotocku go FcivnHudciysTagwexads.Koiwyoy.
Mcefori hhuzpKegc() nnat loe ica HnihgCujnolhDulxemevj.Qiashix , ryeps rkuefon xqi ChoykWuycormQornoquzc abmhamva ze dsaki ot cbidhBusmildBoqficuts.
Vjufequ lmub() wa mujon LxirxMiwsojyVaxfasoph.
Sou eqme ist jketmx ki acaov tyiadedn uljecorqefw JwilxQimgixfGeytifuyq exzbelsuf ak xue ijbuke xwahtBerk() gozcitge latow.
Baq, luu seif u dsage fa lih BtomvBevkizzBedcuwaxpVuwesul ogb gugt ok lu PdagpavPyeyu. Wee’kv ore HeaxUgpahesp mud nxil. Akec RaekIjxolacv.pf ap oi.vain uyz bborwe in nu:
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.