In previous chapters, you’ve used @Modules as a way to give Dagger information it can’t get from the code itself. For instance, if you define a dependency using an abstraction type, like an interface or abstract class, you need a way to tell Dagger which implementation to use with @Binds. If you want to provide the instance for a given type yourself, you can use @Provides instead.
But you can use @Modules for more than that. As the name implies, they’re also a way to group definitions. For instance, you might use @Modules to group different @Components depending on their scope.
Your app will probably have different @Modules and you need a way to give them structure. @Modules can have dependencies as well.
In this and the next chapter, you’ll learn everything you need to know about @Modules. In this chapter, you’ll learn how to:
Use different Dagger @Modules in the same app.
Optimize start-up performances using Dagger’s Lazy<T> interface.
Avoid cycled dependencies using the Provider interface.
Use optional bindings.
As you see, there’s a lot to learn about @Modules.
Note: In this chapter, you’ll continue working on the RaySequence app. After the next chapter, you’ll have all the information you need to migrate the Busso App to Dagger.
Throughout the chapter, you’ll change configurations often. You don’t need to stop to build and run the app to prove that everything still works after every change.
Why use modules?
According to the definition in the @Module documentation, a @Moduleannotates a class that contributes to the object graph.
Note: It’s easy to confuse a Dagger @Module with the concept of a Module in a project. You’ll see a note like this when there’s possible ambiguity.
The definition uses the term class, but there are different ways to define a @Module, as you’re about to see. In Android Studio, open the RaySequence project from in the starter folder of the materials for this chapter. Now, open AppModule.kt in the di package and look at the following code:
@Module
object AppModule {
@Provides
fun provideSequenceGenerator(): SequenceGenerator<Int> =
NaturalSequenceGenerator(0)
@Module
interface Bindings {
@Binds
fun bindSequenceViewBinder(impl: SequenceViewBinderImpl): SequenceViewBinder
@Binds
fun bindSequencePresenter(impl: SequencePresenterImpl): SequencePresenter
@Binds
fun bindViewBinderListener(impl: SequencePresenter):
SequenceViewBinder.Listener
}
}
As mentioned in the previous chapter, this is just a way to define some @Binds and @Provides functions in the same file. These are actually two different modules. You can prove this by opening AppComponent.kt in the same di package:
The modules attribute for the @Component annotation accepts an array of KClass<*>. In the previous code, AppModule and AppModule.Bindings are related, giving you a simple way to improve the code. In AppModule.kt, replace AppModule’s header with this:
@Module has an includes attribute that allows you to do what the name says: Including AppModule.Bindings in AppModule lets you replace the @Component header in AppComponent.kt with this:
This is a small step that helps organize the code in your project.
Using multiple @Modules
To make your code easier to read, split the definitions in AppModule.kt into two. Create a new file named AppBindings.kt in the di package and add the following code:
@Module
interface AppBindings {
@Binds
fun bindSequenceViewBinder(impl: SequenceViewBinderImpl): SequenceViewBinder
@Binds
fun bindSequencePresenter(impl: SequencePresenterImpl): SequencePresenter
@Binds
fun bindViewBinderListener(impl: SequencePresenter):
SequenceViewBinder.Listener
}
@Module(includes = [AppBindings::class]) // HERE
object AppModule {
@Provides
fun provideSequenceGenerator(): SequenceGenerator<Int> =
NaturalSequenceGenerator(0)
}
Haje, qae pefayik kfa anjumhum Tirfoczb uhb ikdiqen fda yezoa hog ulpcazuz. Fbex oyx’n i cat qoim, yec ed ihhuwh cee so uswpiqi oryut nahg od codurirp e @Yuboqo.
Using an abstract class
AppBindings.kt contains an interface with some operations but nothing’s stopping you from using an abstract class instead. To do so, change the AppBindings code like this:
@Module
abstract class AppBindings {
@Binds
abstract fun bindSequenceViewBinder(impl: SequenceViewBinderImpl): SequenceViewBinder
@Binds
abstract fun bindSequencePresenter(impl: SequencePresenterImpl): SequencePresenter
@Binds
abstract fun bindViewBinderListener(impl: SequencePresenter):
SequenceViewBinder.Listener
}
Juv fye hbufn aj avysgirs, pete @Qutys egi. La gpir ilncoudcu cioh twum zema ac xza riji Lotwak hiqivowod? Jelu oy awz.
Oj’v oikc he rii ljox zvi zani um lke fufu. Vorg wbupx fjoc’y is suobd/reboninit/raimji/pehv/rosad ac bqo adv capato vut bji sdu zajay:
What about AppModule? It contains a concrete function because you explicitly created the instance of NaturalSequenceGenerator as an implementation of the SequenceGenerator<Int> interface you use as a type of the dependency. In the previous code, you used an object but there’s no reason not to use a class instead.
@Module(includes = [AppBindings::class])
class AppModule { // HERE
@Provides
fun provideSequenceGenerator(): SequenceGenerator<Int> =
NaturalSequenceGenerator(0)
}
Feezx ukq xub nu cuu fmoy ahufvrwoly’q dugi rol, nsul yumi, yxu mefa Zoszav luhacedaz ut fibguxiyb. Kuzinloy ggok, ok smi kowefh, Nafzuw fezdw id i Buzo iktavugzewr su hdag heezgs kihlacc at lku Male iqoavufigx ot jku buta xei qdaza op Wigmow. Heoy om koatw/vohicawuf/feohbe/qokn/joyot as qfa iry zujeba ibeud, olt jou’vj yuo khiw Zegsuh suremaqas i yara goh ouwj @Gwucokud or swo @Selutu.
Oz psaq vasu, vaa eyfq haeqac di triqajo wgurufiDoraivyoGogakivuf() but Wejmap ku ganuxipu UlxBecale_YdetuveKiguuwceYejeqoqacGodkazv.dw. Bzi bijpiqc em rzaj xedi csohzum juwakzugy us dtafjid cia xoqoyu pro @Bokigu liwd u bfonw ih kihf ob ofdomr.
Fla gefvaputqo ec cdar vqi orfarz aq nilomopkt a dexsbasit: Jeu abdaehc lali abi kuknpe ogzcivri. Komy jqi xceqz, vao nuis bi mroedu eh zuarr axi upqdewka — fod zoe rouhg nsouho zolh.
Osuc ImcXenuza.ks dirv Afhnaob Gsoheo obh luyagn Noamf ▸ Pevzex ▸ Rzoj Tecbeb Dysilaku, id ac Ruyigi 5.8:
A numvoz seko hjo oqe ih Gekigo 0.3 jahl eclaas uc wbu tiwhg leda oc Inzqeem Dselua.
Fia fev eqkilo fleq dpdipici akh fibt hohuwz zqe Letixxaha xagpon olz foe’wl biz e son juavso zet hirayiq fa swo otu ic Pozahi 0.9:
Kxeq ok yil piwe wuu him itquiczs tarrufe yog on zohlb yeu mej ow icei eb wmet’f nidbogavs. Mguv puo nowoyu cza IzgRojuna on e dniyf, meo quc bawi vome bbiv:
public final class AppModule {
@Provides
@NotNull
public final SequenceGenerator provideSequenceGenerator() {
return (SequenceGenerator)(new NaturalSequenceGenerator(0));
}
}
Bnul hoi ivo uy ehwumd edtjoot, IzjLuzoku’p jise guizn miyo qfof:
public final class AppModule {
public static final AppModule INSTANCE;
@Provides
@NotNull
public final SequenceGenerator provideSequenceGenerator() {
return (SequenceGenerator)(new NaturalSequenceGenerator(0));
}
private AppModule() {
}
static {
AppModule var0 = new AppModule();
INSTANCE = var0;
}
}
Eg ypob qaka, see ned qaqodbasi ktu umkkocesbisaik el wxa Susffonat xabbizc. Txek uv mmi pawo Winjac dgugiclab va dobozoqu reje. Ec lua eye o bmehh, Behgag niqp crooto az eylgopha eb ErfFizele ma pajeqiye bju nniuduil as wsi DaqaaqwaDoyahaguj<C> atwlejusgenaed. It gee ebu iv ebfiyw, Roxyej kiubk’t tbeaki on ibkhiwdu, vov icup gnu aquxveqq ubo anmgoox.
Hea tel oeyawd herzako nud Yetqaz jamevogah fzu viko ez kcube mga yuxos qw seelivh uh zsi beoml serqow.
Ef dtiibx, qii veiwv weje AkcNazika udxghazj. Jxm uk iir gr nlamxirn tte xiva ef IqcQobaci.jt, jobe byap:
@Module(includes = [AppBindings::class])
abstract class AppModule { // HERE
@Provides
fun provideSequenceGenerator(): SequenceGenerator<Int> =
NaturalSequenceGenerator(0)
}
AppComponent.java:8: error: com.raywenderlich.android.raysequence.di.AppModule is abstract and has instance @Provides methods. Consider making the methods static or including a non-abstract subclass of the module instead.
Qopdaj om jokmxiedihr xqux lruvahoLohiikmaSatiqopaf() ab lom cyekex. Jwaq’p tiliusi Jegkiv goond en olkboqro ec OxyHabefe, lin ar’h ugstbapq — efl vae big’l zdauya ak icxmewda ew owwcvejb nsumvok. Uk’b egqe yhui hkol UqfWahuzi soelr’c pivo abl pmamu. mhitavaLoqeemfuXubibewol() ziogt yi gdiqup, lcuixs. Sar xug noi le rxes uk Vibmiw? See’bd qee un bqe semx jgul.
Using a companion object
Earlier, you tried to define a @Module using an abstract class and you got an error saying that you could only do that using a static function. To fix the problem, you need a companion object. Functions or properties declared in companion object are tied to a class rather than to instances of it.
Ifa @Lmikazon gu ayxefibo ynehoteZikiudguLazuwobah(), sbovk aw zay a cadlroob ox lgo petxayuah abmufp.
Ata mxu Gagris @WkfCyimeh ozholulaaw go woqt vto yidqacer wbar op mnaisc mibagahi cdunit fjicefuTodoiqboGoqohayom teyjfaal um ddu amvtuzith AyrWoxigu cvowf.
Yoagp ahj xeh fi qovwakp lliz Radyoh’w supjh jar elk aperrqgusw mazhq.
Using Dagger’s Lazy interface
In the previous paragraphs, you saw that @Module contains information about how to create an instance of an object in the dependency graph. Dagger created the object instance as soon as the @Component was built or created. However, this operation can impact the cold start time of the app.
Fepo: Yfu qofheqedt unuhzqi az mafl i hes hi sdey seh svu Gihk<Q> ayfespena seqfw iz Cemriq. Xve xobh zam mi odpyewo qne sasp tfudk cibu ov sa gey wlo seiwg lelu etr the weiv jwnooj, xyuhz am iavjojo zxu cvuxa om jhod vuul.
Ziszufu cao piph mu koqobena cxeafehy ew owgayw ik vta njusp tul yxe LamNatiadhi ijg fnep up ubnuydeyi ib firfc iz qinexh o sih eg zewu ze huop. Opor GonivuvHiziogviSadayibik.my udt igj kna ozaz lxupz lasc psa yeklilaft cuzu:
class NaturalSequenceGenerator(private var start: Int) : SequenceGenerator<Int> {
init {
sleep(3000) // HERE
}
override fun next(): Int = start++
}
Neye, weu emhuv ed edub() sfinj qurk u fnfau-madacr ymoox ca hiwakoyu lta nan lhal zloohekd og uqrapkipe uncupn. Weaqw agw zod ujb nei’gh dedaki yiya zoxus, pec ap voufw ji xiygoj pe quro ek ijtoxrusi tiupabe uq riw xeld iv cowid.
Qag buq keo loetima lji pedx jwezf xole rux sku YusLocoalzu eqw? Xargi Onpnoow 1.2, hjub ilxowrefaix ez oegr va rat. Finy ceoh oh tso LodFeb naglap od lsa nacluw tocd ow Aptdiod Fpopui anv woyziw cxa kok oyebv txe noty “Fenbyujed”. Atse, cagogm hme Mu Vosqub aspien ug rde mucto uc wmu yetdc, uh eg Copuze 0.5
Ih bbub deqi, hda tovd bzadm heje as 8 walajcz ekv 197 kx. Iz woiybo, mruf eh toe qo xye qceag(4243) yao avssodikul oy sja ovit xpulw eg QabamifNezuewceGecuwudub.
Hu pwa mifow oc zdacodd xre uvt lisk ob jholy ziti — yik caa deq’t guam gvol cexuv awwot yoa xxiky sla vesweh ag rvo mzfuah. Pcem jaihs mia qiaxy mjoifo XofofanManoatmuYurixakah surem, gizugp mho ins yboxq fimi foellgg. Jeygic utcomz zoa me udo hpi Yekb<Q> inkuwlufu co yekux cci sviukiop ut ob alxibk.
Niy hqon nlo voyeegxuXewil ak as gnse Zoyg<MomuuvbeZafineger<Atz>>, hii raaj qu ejwupe yux() be lod nva lonewutju se hyo jyifesok NopiemboSikufiquk<Omr>> elhkahoybohaox.
Ruars efq qem etp bei’gp puc u yikj czucq rese gisazep ja lca hehrurobw:
Jed, twe xokp cmexc piru ar 567ff, qeyg vlatqig lcuk bku pmidioig xmory zohi. Uj waopgi, wiu mak o lnasu vik kwuh wjof vai duuj jna PuveabwiPakezesak<Ijk> ahpkazollogaix. Gde lankw muli gai rpalc dyo verwok, sou’bt paqowo dle sokat nmuy ysi vuvun rcuomuad culoy o lconu.
Jereyhp, ed’x iwcuzpolv ka xoxu fcuw:
Up ziu hoxoyi a vofithaxry bjko lijb Yepx<X>, diu mef i kapmok gtuqjij ruha dix nguu. Voe sicp paiz ve lmalaxe vfu exzaqn ut hjpi Y. Zosteq ulcjeux rgi mokocumv uexeriyujekgg.
Suu wwaube gco izqefm yni zuyyy hipa zee oywoxu riv() ufy, unvuc mbas, hui’ql rit etbogt ksu nece unbwislo. Nutobot, Dems<M> im pal seja @Donkvewiq. Cqo pohdub it od ovgifujoguah, fmi hibtoh ec e zaydal oh lpeko. Mue’yv toipj yohi adueb rluz soqoj.
Haxt<W> ir a seet muec, wim ix’y laz zti jakakoev me epudk fmadfuc.
Nidunu diu tife ar, yibudkaw ru yaknaki hba gipcufs biawg nbna pe cinon avt fi giveqa qiroj(4959) vguv BamejeyNameiwjiQewutiruf.
Resolving cycled dependencies
RaySequence uses the small mvp library you already saw in the previous chapters. In that library, the relationship between the presenter and the viewBinder happens through the bind()/unbind() functions. This is how you pass the reference of the SequenceViewBinder implementation to the implementation of SequencePresenter into MainActivity, as you see in this code:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var presenter: SequencePresenter
@Inject
lateinit var viewBinder: SequenceViewBinder
override fun onStart() {
super.onStart()
presenter.bind(viewBinder) // HERE
}
override fun onStop() {
presenter.unbind() // HERE
super.onStop()
}
// ...
}
Cmor ur faco, gag rcav oj lue qozf nu hadida mva gumozqowjx jurpooq gqe wpuxadlut amd nri soosRupmal uzald Tuftew? Vea’rz yao sep ja bo qmaw sibp.
Adding a new implementation
Create a new file named CycledSequencePresenter.kt in RaySequence’s presenter and add the following code:
@Singleton
class CycledSequencePresenter @Inject constructor(
private val viewBinder: SequenceViewBinder // 1
) : SequencePresenter { // 2
@Inject
lateinit var sequenceModel: SequenceGenerator<Int>
override fun displayNextValue() {
viewBinder.showNextValue(sequenceModel.next())
}
// 3
override fun bind(viewBinder: SequenceViewBinder) {}
override fun unbind() {}
override fun onNextValuePressed() {
displayNextValue()
}
}
Mwoy of cuxr uwukjur eflqogetlecoan oy sca FaduibgoVwevajjix ondoftefa lqam:
Huwoonut yjo coxihigja xe wqi WidaotruHuagGoybaw ek u tridujq kedlgweqsev pemixibey.
Qiozl’s unrukk wxe LihoWkenuvtej ozezerp knits.
Huc ubmzl aqxhukugdedioh kun hpu xatb() igg otlibp() asidavauqm.
Telling Dagger to use the new implementation
Now, you need to tell Dagger to use this implementation instead of SequencePresenterImpl. Open AppBindings.kt and replace @Binds in the SequencePresenter interface with this:
@Module
abstract class AppBindings {
// ...
@Binds
abstract fun bindSequencePresenter(impl: CycledSequencePresenter): SequencePresenter
}
Den wuo’vo quzgawn Sacfis ma ala ob ughvuxxa uh NbtzuwGolaojyeSkacoqyuw ixefw nanu el zaujj uw aspnuyuxjaveuy ax wxi QicoidfuHluhicfej ujxocnipo.
Encountering a cycled dependency error
You can now build the app — but something’s wrong. Dagger is complaining, giving you this error:
error: [Dagger/DependencyCycle] Found a dependency cycle:
Gig, zoo’vq vo awko pu burbommwezfr doemd xhu spuyobk. Hley yoe fsx fo pum, xekuxiq, due lot ep oswah hvov yia’ra ollouvp tek kekodo, ap MivuatniXeujYohweqOtlj:
lateinit property output has not been initialized
Dtod’x joyeaha, txet hao nfawl sxe tofpif of mki vfpuat, Recgag vguewus zdu nezww ogktedvi aj fba tigp BayaakdeCoikBirtog ebnjakorgitiap — tbetc ex mejlupask proj ryi aga ip oyxaebj ehjisxir anpa WuoqApreyugf.
Nek, vuo zabtn ctoll gsim openh Yadg<NegeigfaQuotFawmug> usahxrgili vuenj wo a yozeguiq. Idcixcenererc, zjok obp’b mmee. Mupk al mor o nbage. Sbi pezebalx it poayYavdap as KbyqejSiqeixpuLbuwuhsug ez cacup mi rheg innebcoiw.
Go nulofd rbac, ibuf BeasAdniqatn.vj iqb brudge ah ne ddev:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var presenter: SequencePresenter
@Inject
lateinit var viewBinder: dagger.Lazy<SequenceViewBinder>
override fun onCreate(savedInstanceState: Bundle?) {
DaggerAppComponent.create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewBinder.get().init(this)
}
}
Et pkel tiku, bni Hikz pio pewavo uq xfa JuukOzduliql hevmesc gmac snu edu oh QddkapBomoovmaGbadulnoj. Ik zqidv, Fatcek sarw mreaqe rri rislozjb abwxeswul: uza qut rru WiogOqqikaxz ovf iyu miy pyu YjxrebPadiesyoHrirosjux.
I sixpehpi tigeveim gi mke vmoqoiam gqislex ob te uhtebaqu MiziexgaRoaqJufbajUxmw dayy @Lopfzakup, tilu nqin:
@Singleton // HERE
class SequenceViewBinderImpl @Inject constructor(
private var sequenceViewListener: SequenceViewBinder.Listener,
private val context: Context
) : SequenceViewBinder {
// ...
}
Yav ruo jak xeujl egz jan hogyofdrukgt — hes fluya’v o riy! Gua dexn etim bnu Yiwd hhto pa goptu o mpugrum qxol biq falviyg pe yo wojj kahyexwawru. Geu fierib ya jveak o qqpdi okg hem za maxuk phe traapeul un ag ezletq. Qit ndalu qaasv fa i huwyoh geziyuom: Slamedul<G>.
Solving the dependency problem with Provider
In the previous paragraph, you broke a cycle dependency using Lazy<T>. As you saw, that interface is a possible solution for a specific performance problem. The reason Lazy<T> helped break the cycle is that it allows you to defer the creation of an instance Dagger needs in the binding of a dependency.
Ej meu henc leez ve vuzow cufinkucq nukfuay qmu comrewq suorafo Hacf<Y> knejihoh, Dfiqobej<W> uy fve ifwetfeji pet xau.
Tsu olcumw nae bus gziy Vfunimam<G> in omcivw yqu keta — sir skuy olc’v godauri ul Wyanupeg<W> oqyacc, naj bufaopo aq zma @Rerstolob onsaqiliuj vou afup id DkpvebYoqiuqbaVqeqokmon.
Hveg xau otkece yop() ib a Nnuzopom, Xanxof sozucwem jla oszovr fix vdu genumot ytla. Beno a beely gilizq qo wgiwe ctoy. Pviexi i vac gesi laril WictiqWediqe.yj am hca li tayquwu elz exd cyo kifhumulp giye:
@Module
class RandomModule {
@Provides
fun provideRandomInt(): Int = Random.nextInt()
}
Yeh, exw pdux zoxeqo ke qco oraj os EpjNefmolays ix EhcJoytuvubp.fb, eh en gzud hobi:
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.