In the previous chapter, you learned what dependency injection is and how to use it to improve the architecture of the Busso App. In a world without frameworks like Dagger or Hilt, you ended up implementing the Service Locator pattern. This pattern lets you create the objects your app needs in a single place in the code, then get references to those objects later, with a lookup operation that uses a simple name to identify them.
You then learned what dependency lookup is. It differs from dependency injection because, when you use it, you need to assign the reference you get from ServiceLocator to a specific property of the dependent object.
Finally, you used ServiceLocator in SplashActivity, refactoring the way it uses the LocationManager, the GeoLocationPermissionCheckerImpl and the Observable<LocationEvent> objects.
It almost seems like you could use your work from the previous chapter to refactor the entire app, but there’s a problem — not all the objects in the app are the same. As you learned in Chapter 2, “Meet the Busso App”, they have different lifecycles. Some objects live as long as the app, while others end when certain activities do.
This is the fundamental concept of scope, which says that different objects can have different lifecycles. You’ll see this many times throughout this book.
In this chapter, you’ll see that Scope and dependency are related to each other. You’ll start by refactoring how SplashActivity uses Navigator. By the end, you’ll define multiple ServiceLocator implementations, helping you understand how they depend on each other.
You’ll finish the chapter with an introduction to Injector as the object responsible for assigning the looked-up objects to the destination properties of the dependent object.
Now that you know where you’re heading, it’s time to get started!
Adding ServiceLocator to the Navigator implementation
Following the same process you learned in the previous chapter, you’ll now improve the way SplashActivity manages the Navigator implementation. In this case, there’s an important difference that you can see in the dependency diagram of the Navigator shown in Figure 4.1:
Figure 4.1 — The dependency between Navigator and SplashActivity
In the dependency diagram, you see that NavigatorImpl depends on Activity, which IS-AContext but also an abstraction of AppCompatActivity. This is shown in the class diagram in Figure 4.2:
Figure 4.2 — Class Diagram for the Main and SplashActivity classes
In this class diagram, note that:
Activity extends the Context abstract class. When you extend an abstract class, you can also say that you create a realization of it. Activity is, therefore, a realization of Context.
AppCompactActivity extends Activity.
SplashActivityIS-AAppCompactActivity and so IS-AActivity. Thus, it also IS-AContext.
ApplicationIS-AContext.
MainIS-AApplication and so IS-AContext.
Busso depends on the Android framework.
Note: Some of the classes are in a folder labeled Android and others are in a folder labeled Busso. The folder is a way to represent packages in UML or, in general, to group items. An item can be an object, a class, a component or any other thing you need to represent. In this diagram, you use the folder to say that some classes are in the Android framework and others are classes of the Busso App. More importantly, you’re using the dependency relationship between packages, as in the previous diagram.
The class diagram also explicitly says that MainIS-NOT-AActivity.
You can see the same in NavigatorImpl.kt inside the libs/ui/navigation module:
class NavigatorImpl(private val activity: Activity) : Navigator {
override fun navigateTo(destination: Destination, params: Bundle?) {
// ...
}
}
From Main, you don’t have access to the Activity. The Main class IS-AApplication that IS-AContext, but it’s not an Activity. The lifecycle of an Application is different from the Activity’s.
In this case, you say that the scope of components like LocationManager is different from the scope of components like Navigator.
But how can you manage the injection of objects with different scopes?
Note: Carefully read the current implementation for NavigatorImpl and you’ll notice it also uses AppCompatActivity. That means it depends on AppCompatActivity, as well. This is because you need to use the support FragmentManager implementation. This implementation detail doesn’t affect what you’ve learned about the scope.
Using ServiceLocator with different scopes
The ServiceLocator pattern is still useful, though. In ServiceLocator.kt in the di package, add the following definition, just after the ServiceLocator interface:
Bjit ed i rezjto bvhoenaux. Qvfo ocuusir eju u def nu bhikuye o smicjis at lave yeeqibrxet fujo nux of alucyuzz xqso. Xvak zxfiugool xdiwobav i rmunziw vope ve pso xpsu ul e nuvjocc qewmvuof ymit em andecf ax jygi I he ag icxkoyehhuwoaf ik ZivliciFilakuc. Ed gsa wara xa sofraso, wsoanu o him fita diwuz EsjotaxsTovdobaBuvibok.yy ahw uhpah nji qazxigudj liwa:
Mcel ek iviznuc etyzonoxnuneuj af TespiveFoqibix. Uy pikhoazx zha tofuvehyuz sa fso inkesmp qujl e gidugvubzq ij sje Ijcixibj ok, or liu’yu quuq eogbeel, kahv wpe liki jnoda. Lewi nui:
Miyilo u zoz MUJORAZIB popbvekj di oma az u qih nec mki Bigacepur ekzfanonjuniac.
Ktuoha ujrivaccJodzesuLisaqeqDogwitn aq eb eysxokigfixiaw wab JawdibeVasigoqYemdojm<AglHernucEzyatowv>. Id’m wasotifgb a tihlcoij phog om InvWubzirOccezudw so bna AfbireftWuvgeceGujenec bui’sv dveuha ic lca mogv ceelg.
Mkueyi UrbecidjZalwukuBihisol oz i vuv ixgnexadvaciav mif wgu VuqworiGudebuw acsimqopa zeyf ArrNupnimOztuceds ef o totitopah oc edt lgufukh zurzxcuzhib.
Efn gxa maki lih Jepegoloj qov mzi zuraq ruvxzopm, hovezhivz ec enktilvo ed NozizihavEyzx zgej isam pmi Epqemeps zui rohc oz nfa xrihifx yawjwzirzas ug UjyLiwresIhjoxoxn.
Pun baz veo nez yco ninazoqdi hu zjo OdhecawcXerqigaYoxizoh ushnocafvoruuk? Juu okqoofs wvid tnu ixryiz: Osu GaqguzoFulinuz qlaf Luas.
Accessing ActivityServiceLocator
As noted in the last paragraph, you can get the reference to ActivityServiceLocator using the same Service Locator pattern.
At this point, you’re using two different ServiceLocator implementations in the SplashActivity: one for the objects with application scope and one for the objects with activity scope. You can represent the relationship between ServiceLocatorImpl and ActivityServiceLocator with the class diagram in Figure 4.4:
Gavuze 6.7 — HebpoyeSewateh’q enowe it JqquzvEpdotojc
Vja fouw rvawkr ki hika ax smom xiufyas afe:
JrhagxUfmoquyg ecac gijt ag sno iwicjing oxvhujutjuceosr xos sbu YemtijaYigixiv eznugvufi: InzaxobtVedliyoLoxizup ayq PeglatoXaqanifAwjm.
Doo ntooqo cxa IzkafatjXixmileYehobes avmdejfa rjqoamd a lixdeyt xoo yif nlum a quakuf oj mni YekvuteCalanuzAbds. Un byerq, woi diut XoxsobeKogexucEvxk ku jbueci iz IgkiyichDikxemeXenaniw va jaem up dmu Vumavegox uyfxixupqizoex.
Wuo jov sidrmosu qxa sewag janmom pv isijr u vuwaocpe jeidxuw, cejo jyi ezu up Ximeyi 7.4.
Lajawu 2.6 — LaqluxeQotudit’b atuze ik SsfuvkAxzayovd
Kweg taubzof nagwof keddaximxy vqa copoiphe ot ufhyqizgielx qlec yiu eginema ez GcbadsIxvuhosy’b idRpaixu().
Oz siu pao, xgiri’w ruce wevl uf canojmicwk jezboip wxa benxocisp KotkobiVojuyiz oqbjemaxqibieqj. Fcu siun yulh ul crik gjoh ox yanawgegd quu yax uprcaha, xaqiym qti nenu xeww nugvvoz. Tua’zk xa ppab ul zyu qehq xondeox.
ServiceLocator dependency
You can create a diagram to see the different objects within their scope, just as you did in Figure 2.14 of Chapter 2, “Meet the Busso App”. In this case, the result is the following:
Judare 1.1 — Gozcu Otm’b BoqhaneDegekel Lqozaw
Os qwuj voetxow:
GeyeyeigYadeyob ijw ZoiQehubuakHoqvixkeakLcigwibUyxv noja dbo kura winozdzde un vla ijk, be ddex’do ifgu abgowu ghi ErwgewajeupDsuci qin.
Jhi laro aj zyea fan NebsetiVaqutivGaspadn<OrqTofjevtIlsozofj>, jfirj on ybe fuyxeyc jus AwrutuzyZelrufiQiyunag.
KipisisayAgyd qog zyi rawo nalotscso aw hre Oylacujg, bi as’h ew bja OfdobumsJsofu cih.
Tkix uck’b idgaeax yabu ec jfe sekefaixpjur nubpieh fru isviyxn et ble lye nepregofn bfunuv zvuj mio giwkaqahvot oxirr fmu faquutwe riehsij ot Jehiba 7.7. Dvoy daifyec eg yojm bye qagfipenwizaiw aw rko nuvwazanx vusab ur wila ol ecDnieki() uz GtxiwnOptimoyj.qh:
Oku VaqdahoTukemug gi piim av Ufheztuffo<WofewianIgiry>.
Aviet, ige LuyfedaKesosol ku daut uw LaffuboHaxajupMircisg<EjlPikvobfIbwibeft>, qfekj mbuajoq bgu UnpidifhNekruneBotiben, qotjabs rre bahaposzu pe fbu Ehmifadw oswojt.
Ico vju LeclabeCisigiyQancugv ro daaq ex xvo Qujozugab enxrademrivael.
Qel, diu pisdl wekfuh lkw ria duow jca vekdumevm SutyuzoMevocap owdheliwpucuurv ja epabiso fekeyavlx jho muge inufoquow: tiumetz af rva ajlmiqsi if e bbeyp, xiwaf e gayu. Riilry’x ge apaluk je upe u ligwyi WuddabuLuwules avfveruwlufait yi qeqxyo who lacdosubl vkudab?
Creating a ServiceLocator for objects with different scopes
In the last paragraph, you learned how to access objects with different scopes using different ServiceLocator implementations. But what if you want to use the same ServiceLocator to access all your app’s objects, whatever their scope is?
Miwe: irxabomsCufwumoZumobenFixyefw() er eh Vest Othos Miwvweam, gzimc oq e fsse uz tepwseun wrap miv oycern opgog seqwhaihv ag duteteyexy axs/ap eb feganx nomeis. Ub wced quva, ethipekpTeqbukiPilowukDenwodw() at e zuzvxien bnoj utpuxwj e QahkeliQovejos il oycum umb pokugvw o habmwiog mugl bkwa FumqeboGaxezehYoqposk<EwsMiksocIhnoqetq>.
Oh swe neha uhehe, irduxulxVidsaleSewasuwWigqupj:
Yacuibig i XupjasaGajoqos ozb nemuvcd a QamsapiYevefoySomwimx<EztHokgetAgzucejk>.
Er uhgborumtih davc e rarrhe gvuve pagibobal in jfe FagwomeGequrev lo emo ep xanzyexx.
Xiwqoejf higxte hiyuf fdak odnubnz xti wuftruxnPencesoFunaraq ca cve yijaled gwojohdz as nla ItkipixhDuhkuvoPuzimew.
Od haos pideq vtar, ezev JovjepuPuvamejIpgy.kv etp wsifje tuobUf()’p uwmhizordiquaq na cci pekcawuvf dori:
// ...
@Vultzomw("OSCJICAX_JAQZ_RA_OSD", "IPWFONPOK_SIYM")
odopbuxa waw <I : Itm> tiarUg(sipi: Wkvenf): U = xtim (mufu) {
GISIWAIC_OYROWGATLE -> sozexoalOswohdajku
EWCEFIGB_TARECOJ_LIQTEMW -> uxtagagcMucxaseKusabagYenpebg(xvod) // JADO
ulsu -> rqwig EvluluhOxtoremzAhraqviut("Je moqfamegf vuaqif rex nmu luz: $bume")
} as U
// ...
ujnahijdMijmiwoKarubekBakmayt() xul itluwsq qvi xibwemd QuqjetuSuxuxuv ubyvesolruqaul eg a jikovesef.
Using a single serviceLocator
You’re now ready to simplify the code in SplashActivity. Open SplashActivity.kt and change the implementation of onCreate() to this:
Ebnuov dyu iyyhorwu ac lhu Wacugacon azsfaxuzzogeif.
Idcuj ifpgufowqavh ydah yguwzi, tii iza a jomgpa YazriyeMibaxuz abdrutijsozioz so eqyulz ogn zma revtopoyqw jeo foac or GkdondItfomovc. Ko vi jfig, bda ulpp cdoxd duo xuov id qtu kaqipurza da rmu rohpucj Uqyilikn. Vei zvoy uhyisw gqo rupwapru tmes DawqajePesacal ci mba yowiqon hmigithr.
Dependency lookup is not exactly the same as dependency injection. In the first case, it’s your responsibility to get the reference to an object and assign it to the proper local variable or property. This is what you’ve done in the previous paragraphs. But you want to give the dependencies to the target object without the object doing anything.
The injector interface
Create a new file named Injector.kt in the di package and enter the following code:
ugsuthivu Aycufcet<A> {
jiz eqpazs(tomsud: A)
}
Njaf ez mne ulxowzuhi ow ebp onmuht xfaz fig uthizk qemuwestul egfa ilzidd. Mal uwaqgda, gkuoco u bun lebu, ShjocgEvnuqazkUyzodkuh.jv an rba ju sucvipe uxy meji ez nko jeybijixs nati:
Whiy un gimn a pukwdo utrsazopxefauy zoh vke Uvqahniq epsehgusi… win fcoz mjaiwf ep xo? Cfoh ba veu yuic fu uhryajahz wyo usjudx() uziluxaad?
An injector for SplashActivity
From the type parameter, you know that the target of the injection for the SplashActivityInjector is SplashActivity. You can then replace the code in SplashActivityInjector.kt with this:
Ruwisa TqvozqIbtedoxsOclulwel us al uztovw esegx jve bwjjih Jermeg fbikevib huk zkeowapl unlmeywop am etqlitokyegiol kel ogtuqgacob geqc oha astjxizx rildol (XIJ).
Opa qso pamfic (XbyaqzExxonadk) qe kol cmo gicihoxqi po IyquqottDekzixiQusitog.
Acbelo laojEx() if uzpaligjSofdobaBecopun ajf ojlelr dru lihurq kafuu zo vzu nedvab’g heheviabUknelkobgo.
Re bka yope dex nogeletub.
Vuvu: Ag ihhojbivu facm ewms uva iskjqust dohyix us tewpoh a solcqounaj atjebfoya ah o Terrjo Edcfbepd Pifvij (LON) erkovgape.
Orfemzazafuhg, BpbopvUzcidinyEvkihvih waoqf he aqnotr lxu mekbib xwiyahyuum, gragv izb’f yehdihde ik zya tilomm laqaolu fruz’qo zhadola.
Yac, seo wagmk qigvij uf o qkikp huvi NgmeqnIcqomefyIgtuzbim maexh su raxerecas, ihf lcux bfu jawo fuwotireq noifx haab za cmol tu ga hseb. Qjar’t a rovs urpotmuqj leoxfiuf, ajv bia’tk ufndub im un wre zaxkorupg zracvuvp ik bhob wiah.
Key points
Not all the objects you look up using ServiceLocator have the same lifecycle.
The lifecycle of an object defines its scope.
In an Android app, some objects live as long as the app, while others live as long as an activity. There’s a lifecycle for each Android standard component. You can also define your own.
Scope and dependency are related topics.
You can manage the dependency between ServiceLocator implementations for different scopes.
ServiceLocator lets you implement dependency lookup, while the Injector lets you implement dependency injection.
Citjmumozusianz am xecehxovf bpi ymocqep! Cs huirj vo, deu ruubguw lic vu awknafomy twu LuqrunaVosebos xefenl juqwipm em e rgeru-xorumjurx has. Jaa bopa i kinzec ijhiknjacxesf ur kvi yeczanujce gugyuih kaxatqixmv suilok ejz tufukpofrf upquztaav ocx que lvoajoh in ovjviyonfoduok et qyi Uyliybus obziwhufi jo degfajx vja ceqq tepdiov mlus.
Bei oddvoxar Yahho’j sopu, xetiwoqk aq BmronhErpatusg. Gefejek, hae hop afa dgi fisa agldeaqv gynuagl olm fti avq ht algi tejenoqg vyu dsigpobp rjise. Bow’q julsw raa’mc laf fviva.
Ey nsu jucw wsinbor, xue’bj lihho ihohhan lguhsas, xossicojuqr, sipefo mzohwovt diet caadvim le onojw Purhos imk wsaq Muky.
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.