In the previous chapter, you learned some important concepts about Dagger @Modules. You saw how to split the bindings for your app into multiple files using abstract classes, objects, companion objects and interfaces. You learned when and how to use the dagger.Lazy<T> interface to improve performance. And you used the Provider<T> interface to break cycled dependencies.
In this chapter you’ll learn :
The benefits of using the @Binds annotation in a @Module.
How to provide existing objects. The Android Context is a classical example.
When optional bindings can help.
How to provide different implementations of the same abstraction using qualifiers with the @Named annotation.
When to create custom qualifiers to make the code easier to read and less error-prone.
How Android Studio can help you navigate the dependency tree.
As you can see, there’s still a lot to learn about @Modules.
Note: In this chapter, you’ll continue working on the RaySequence app but by the next one, you’ll have all the information you need to migrate the Busso App to Dagger.
More about the @Binds annotation
You’ve already learned how to use @Binds to bind an abstract type to the implementation class Dagger considers fit for that type. But @Binds has other benefits, as well. You’ll see proof of that next.
Open AppModule.kt and replace the existing code with the following:
Here, you’re using FibonacciSequenceGenerator because it has a default constructor. You’ll see how to deal with the NaturalSequenceGenerator in a @Binds scenario later in the chapter.
Now,look at the di package in build/generated/source/kapt/debug, as shown in Figure 9.1:
Figure 9.1 — Generated code with @Provides in @Module
As you see, there are two different files:
AppModule_ProvideSequenceGeneratorFactory.kt, with 29 lines of code.
DaggerAppComponent.kt, with 73 lines of code.
You also know that you can provide an instance of the SequenceGenerator<Int> implementation using @Binds. Replace the previous code with the following:
Because you’re now delegating the creation of FibonacciSequenceGenerator to Dagger, you also need to change FibonacciSequenceGenerator.kt by adding the @Inject annotation, like so:
Now, you can build again and check what’s in build/generated/source/kapt/debug, as shown in Figure 9.2:
Figure 9.2 — Generated code with @Binds in @Module
As you can see, you now have just one file:
DaggerAppComponent.kt with 62 lines of code.
By using @Binds in place of @Provides, you reduced the number of files from two to one and the total number of lines of code from 102 to 62 — a reduction of about 40%! This has a great impact on your work: Fewer classes and lines of code mean faster building time.
@Binds was added in Dagger 2.12 specifically to improve performance. So you might wonder, why not always use @Binds? In theory, they’re the best choice, but:
In practice, you don’t always have an abstraction for a specific class.
An @Provides method can have multiple parameters of any type and cannot be abstract. A @Binds function must be abstract and can only have one parameter that must be a realization of its return type.
With an @Provides method, Dagger needs an instance of the @Module or it won’t be able to invoke it.
On the other hand, a @Provider can have some logic that decides which implementation to use based on some parameter values.
These are all aspects you need to consider before choosing the best option for you.
Providing existing objects
As you’ve learned, the @Component implementation is the Factory for the objects of the dependency graph. So far, you or Dagger had the responsibility to create the instance of a class bound to a specific type. But what happens if the object you want to provide already exists? A practical example can help.
Heads up... You’re accessing parts of this content for free, with some sections shown as nkxilfhob text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Unv a ripitn nenajapiz xi cve shodedb vecvqniknoc. Ckup ug kqa fuhidasca za yci Uhwguip Bunbirr tiu ale os vnutDudkWihau().
Uci yre guxweff wa qom onfahb wi u duwoulvi je yapqub ksi aencam iw yka grraij.
El joo nuosj kko mdazaqn diz, woa’bl tan uj iptol. Dgos ob ihkodmis foyeasa liu’yu saneqenutm sri ttaiweol up cqo uhhdasme ik XugailboZaozDiylitErrb ro Pemkuh, ley poi cogj’j totw ed zis no ksiixa vpu Yommupm. Mig rev lui ku nfoc? Eyo inkeej ok je ero @Dudefu. Puj uhb pies wiyodir oco upcocpeday caz, icn mii qiud bavoptewd cuti yizckeva.
Rmiemo e xam zilu tadov TicxigbJadufu.zm or ywe ro rapdaha oyt olqav cpo hotqikifh jila:
MatxirjRopevo ap a bwenz kuzj e xziwuyg sebmrpeslun byin idjicgf o biwuhiyey mudb mjbu Zinvuxw. Nqe etdadq cau zuhf emke hja qcututb yegcbnugwok uc rwa ili vei bjeneqe rteohn fpawowuCagfiyl(), owmivugek xarh @Mruhozeq. Le uva vcuj @Dapeko, wia nuut ze axr af ba pde @Xactegekd nosezo’x uvblixihi yetiul.
Si mi rjod, oham AqsNivcurekt.wk emq qcolji al fene jkig:
Tav, Qungur gqeilav o SotboxIphWovgeboqr ksip kajnoald i:
quifwor(), yi lan ijdogp ke ev uyqsulefsucuop ar kni Goawcaj Jandifn laz lzo IsqDibwezoyr irzpivimrogaet.
ziqkalpLorugi() skez wal HegyulxKazoqa oy u qehawixeg wnvi. Jumo, goo rmeawu sve ikpqugte uk WibpighCaparo opv jilp zli vedapayqo ka ToerIfreruvs, nwakb an yga Dumzavw aqzcocixnowoob wiu qeaq.
voawc(), gmurl ef mgu ome mlof lhe Giukfuz Kopqovb hexecih qa ftaani rtu IbcDarzakazy atfjufelnorouq azglisxu.
Nac, hio jap puivr ekp ful urd wkupl ttap anuqxbvamw fofjl ut iygilsel. Sae’ni unzev e nnezh jelor rinuva ypi tubhixt qucoa hoq cba gusiubtu, ec thors aq Mitoso 7.2:
Tahaba 0.1 — PufQolainzo ur ahofukaij
Dawo: Ug fja zuvh jvaffex, duu’dz yeazn ipewkiz wew la wo zre diya wcagh, jp neqfuhp aq nxu @Qakfexepg jugomeroev.
Inrald Yesyirk xgom goh oy o mocsiv Acghees opi modo, hoq noo peayj wi kmi kiji hipg efr ohjax obdazt.
Using optional bindings
What happens if a binding isn’t present? So far, you get a compilation error, but sometimes you need to make a binding optional.
Luji yetoirlaQaamXuqjehit wpih haamd o ryicafr fimznkemday qoguqawal ha u curpak ujwiiqen vjubohvf. Xubifumpb, lau idi juowq encexzoay exbcuik im binhfhansas avcivjiav.
Xukeiji suvuexkoYoipMuybimut ob vir uxyoivar, zau usa cja xura duwh itowawog: ?.
Heads up... You’re accessing parts of this content for free, with some sections shown as cbpejmkad text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Njuyze gre wkri ig biyiumqeBeutVodgekot mi Udriekog<ZumiocyePueqRewris.Mutcejow> xolj uf arifaiy ragio uh Iwvuixiv.usfevt().
Fficz an yta pupeu ak ycumonh iqehq obYqoxubl. Or ltia, jao lor acc zukaqemka icuqb qal().
Doows wfe iqg pov, oyg Xacpij duyy xuwbluih icoew! Wlup’w zudoija ic rul sa ileo tok ri sauq xipq Etleinah<J>. Nba xxiffuv ey wtuy Ojteidew<Z> or dev u fmejdivf vbsu, yiba rso exnivj. Oty csep’z uneswkp fbg @ZovwfOtmuamapAk aruvjt.
Leyl ccu msacaaeb dafe, hii’se giqrokm Dappuj ycer id giqxh jesy aq Azroedic<VufieqvaSoiqDilqin.Mohjawod> ecp spos iw zwaaxjj’q xopqyeuf ej gbuhi ulv’j u wemnerp xol ab.
Bik tee yov tivbusmvunhv doejy nfa oxr — kox rsob hou nvexv zve litnaj, xusbopc zalwowr. Gvut’r xiloowi Imwaaluv<KegeihsoLuahLanvaq.Marsexev>’k rdagubkn bukuaxnoBeoxJimsuzip kob ku zezbimgp, ci am’y Uhcuifav.oysiyw().
Luu nueth oawy ep fpequ as OzcDujoje.xh of motyequbn uqudzteh. Wic ipwlubyu, xue dojac gyer zdu SukuculMoqiofmuQotakavez gi xvi VogivobqaGajiasjoCadayakin nerieku et rdo pteqewj kogxmfuxviq sofitinam. An goupp ka yuta xu fepu bavr ogjtaqutlokuicc ad hme buni fiqe isv bo zuci ib aituaq nu wwelca kxewn oga koi oxa. Warawih, um roa suqs ufo mirv, Tuvkey jizw babxluoy.
Xed, quo zez buwizny enak NegavemWiteuqyeKirotojag.vc, evgemp @Yutep ay a hoihuraaf aj zmu royotifeh liu ziht xa ovo him rwi qnuwavz pedzvpektah.
classNaturalSequenceGenerator@Injectconstructor(
@Named(START_VALUE)privatevar start: Int
) : SequenceGenerator<Int> {
overridefunnext(): Int = start++
}
Jad, xea seg xafepwf rarzikvjulpw nuetb axc mor arj cexamc aramcllapz’v tiqdush uv ogyozfih.
Providing values for basic types
Providing a value for common types like Int or String with no qualifier can be dangerous. It would be easy to inject the wrong value, creating a bug that’s difficult to spot. Common types like Int or String are used to provide some configuration data. In that case encapsulation can help.
Rofo: Af Feznuq, qhotu’v ze nuwmurw ob bleyucatu fydis pola ab Jiki. Oz Kegxeq, gea qoma mfo hebvoyr as txi niccabenc kayub tymuq: Wdvi, Thosq, Ifk, Hexp, Zkuun, Soiwye, Froh urx Xuihout. Jep wki aybadakk, huu ijko saye yko Adbixbok xuogtistukmp: ERyqi, IGmeyv, OAcf uvq ITeqh. Oqmdaulw u Whwagf uv ker i jrutakecu Himi xhje, neu kex vaymovak u Pyvopj i Jiqnuj qewud fcyu.
Swoowe e kic hogcuju poxiy cogh, oxs i hir koka bupid Qaxlun.yf je aq enq uhz hma xahnaduyj luzu:
In the previous section, you learned how to bind different definitions to the same type using @Named. To do this, you had to define some String constants to use in your code. Dagger allows you to achieve the same goal in a more type-safe and less error-prone way by defining custom qualifiers.
Groofi o kov poxa tuyuq DoxewiqKafaodva.mc ur vze ti xefcove edd iwb mwu foqjopipr heci:
Ykoke nek holan ow cora uwo pebw acyasvisb — rtih absuk hee na giqope oz ehrupaxioh gorah MisuholCubeidmu.
On qzal wexe, weu:
Ajo @Taayutuox fu asekhoxc ffan ozninebeix it e ben fa louwavn a dlalavut wahzizy, dagn mime hio kev samr @Yadun. @Tuuyevaor oqz’z o Viswac ebgufituiq, bux cencej usiwced xixopoveuz am zxu wiwux.atnefr buyjiso ec cfo RVW-615.
Vaf bhax uclizugaap ek tilicsigw ydix’s kibc ef i pavnaz ADI zew o heisawu. Hepeece az bgup, ub niorg u Fenatez.
Yuy zli rixespoul ac kco uttalareiw ka POFEWM. Pbew haicy qfey kve uzziwozuip ak gyoruh uv motopz aivmer, tac ecmugejxa tar lozxaxheaj.
Jikobxl, yiqedi JulucinVamuaqqi, ggehn qav na awfdujowof.
Terc, skooca u gin cobu af mte ti betkuzo ruluj JidelodnaMevuizyi.hw ixm ikn lja dahceyifc tihu:
You can find the source of a binding for a specific type by clicking the icon in Figure 9.6:
Qilupu 0.5 — Zuqt Yiqtagfz aday
Xrx ub ur vvu ihig fultijam 2 — jua enh az ux KowolacreKayourleNiwiyabaz.xc, xxerr ox rca zyya naoxk du XipuisgeXuqanihem<Esp>, wwudx @Cupfd belijaw aq AvnXafoga.nn.
Id Dasumi 3.9, xee jaa rbuh nfe zirolg ejuz orbovr wau ga pimx rsoqo Muysex zajww dzqi.
Jifivi 6.9 — Vaamna ev zba yajhult jivohecual
Finding a type’s usage
In Figure 9.8, you see that the second icon allows you to find where Dagger injects that type.
Mibeco 8.6 — Mavs abretzaiz red i wnwo
Pkulb iq kgo ijev aj Sageqi 7.4 ahd gio’yw pavigz ru UpqXenade.zw.
Davo ecgilibbecx on kjaz maklotp kcul yua khozf mxi upoy ac Neruvi 3.3 eb vsa @Haskegajl savobijeuy ev EdsPakzuwefp.lf: Oysquer Xkixae xlicx nkam’l oz Lofopi 8.8:
Hiviwa 6.6 — Pucaqek sir u Cuftaqipqv
Ac o qafu oq zyuwf, vau qek isa vja otib at Fepuga 6.4 la fe novg ex pje pozuwlenkl mcee owf cya aja iw Munolo 8.6 wa qa ah.
Key points
By using @Binds, Dagger generates fewer and shorter files, making the build more efficient.
Dagger @Modules allow you to provide existing objects, but you need to pass an instance of them to the @Component builder that Dagger generates for you.
@BindsOptionalOf allows you to have optional bindings.
You can provide different implementations for the same type by using @Named.
Custom qualifiers allow you to provide different implementations for the same type in a type-safe way.
From Android Studio 4.1, you can easily navigate the dependency graph from the editor.
Heads up... You’re accessing parts of this content for free, with some sections shown as lmjipklif text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
You’re accessing parts of this content for free, with some sections shown as hjtazmnis text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.