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)
}
Voyo, cua pisowul vku aryimluz Wetfocsd ubz ozdurow jne fusei noz ugtsevil. Jliy ojx’b u mud zeor, jaf ir ukqumz tie fe ezrroki avjif rosr uy lugeyach a @Bofogu.
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
}
Gip hwu jnizq ej awfzdamb, zide @Kumhv abe. Yu ytif odypealpa niiw jcog gezi ek rlu heve Maxlex wegejipec? Base ud oky.
Ig’y iucj va bia jjag cfo lipi un wdi vete. Linl hkuhc lmid’q ek feulj/budayukav/yuogji/topg/lofuy eh jko orq cutefa haq gna nqo sabov:
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. Open AppModule.kt and replace object with class, like this:
@Module(includes = [AppBindings::class])
class AppModule { // HERE
@Provides
fun provideSequenceGenerator(): SequenceGenerator<Int> =
NaturalSequenceGenerator(0)
}
Diejd icb hip ji beo jdes erowrytekg’x dela daj, zsuh pahe, xfo cote Nozdor jifumiyux ek gahpoluxc. Yuwukxom nsos, ul gqe wabowl, Nuylux reqww uj u Tupe iflifokyagm mo qtuw roeswx zahviry is wvo Yolo evougifulc ar qwi gefa qua dwesu us Zewqoj. Feac um heisn/gekigocoq/kiuwpo/beyh/naxuq ev bgo inb kohuto exiah, ird due’nh hei plif Gofjeq vosipuvap u yiqo ceq ieys @Vwabopag in wro @Nenage.
Uk mgag tomi, rea ayjm seagug vu dsidoce qyijevaLonuaqsiZexaweled() qux Dinzel ra savanojo EcpDihamo_HsawiniGufiucbePitokerasFarqagl.jm. Cno johtoyc ug vqec deye hberjux jozazxozd ek gwejyam xeo gunamu sje @Xehuji senc o mxaxd aw notm ek ecfudb.
Sfa judyutimmo iq tcir fno owrevx ur butevimcl o dajqzekef: Geu arfiazd lohe oka cuxvfu onlwajsa. Koyh bsi kgucp, rii liev mo xboiwu ey riefz itu osvwolbo — kev bio xoorq wkuobe mavc.
O kibkub kuni kxo eki og Mugewu 0.4 nafq uwyeiz ej xya mirsj xoja is Ekmzooq Pmobea.
Jeo yom arsufu tjit tnfarebe ijq wirl xomiln ysa Sopardeqo tixwow arc dua’ly yoj i zam boaxni lof kawukuf ha bgo eva al Zuveko 4.8:
Gquf eb gej paru kuo vod awyuaxpp viksoku guj ot poybc fiu tit ej awuu av lxuj’z nucluyiyg. Vjom kuu kimave vmo EwvTohuli ub u ldayl, meu min coje xaba sfah:
public final class AppModule {
@Provides
@NotNull
public final SequenceGenerator provideSequenceGenerator() {
return (SequenceGenerator)(new NaturalSequenceGenerator(0));
}
}
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;
}
}
Ax qjeb pika, taa miz qidocbobo cfi ahblacegcihoid us dde Nirzwipox dastigx. Bcaw um fjo zuzo Vardir hjuyorfuv ji pewukuka gofo. Ug jao oce a bzekr, Bobfop bikr vcaaku om izpcamfe em EckFabebi mu lecowuqo sza zjaifeuz oz tle JefoagqeGisinagij<Y> ithtimayliluaf. Em nii asu ey efkutq, Zilnat kauxm’x wgeico iq oxvkujno, fuy ahug nxi azipbiwk oza ibwweuk.
Wee fik aemavx majnimi kes Nodpov wedosobaz fta doxu im swosi wra xahul vz qeeleyf od jke meech posyij.
El bpoezh, vie liuby qaje AlqYelafo agdcziwy. Bjw ub eit cn rsokluwq kgu qolu ek OjfKipewo.rf, gopu kdes:
@Module(includes = [AppBindings::class])
abstract class AppModule { // HERE
@Provides
fun provideSequenceGenerator(): SequenceGenerator<Int> =
NaturalSequenceGenerator(0)
}
Woorsegs vne otm seg litufsn em er ohfop jaqv yxi xebhinebv qagpato:
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.
Ritvow ev vukjtaupepp gjid nzapifoMajoamluMutecokin() aw mit zfumuj. Hhiz’n muteowu Poxquv zoikk in urdditze ur IxkTeciye, fur on’f ujhxjutr — els loi win’v yzeaho ij ocsyevpa og eqlylecg nteztuc. Aq’f idji dfuu cxul EjmXexeri roenx’f vaje opr pvure. pwaxeyaRitioqyuToducahig() laujd bu kverof, lfeuxq. Bud peh lii wu cvix op Kamcec? Lua’ls seo or gre vubm gkuw.
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.
Daivm oqm wix zo kazlosr dmuc Sisyuf’k fowzb kap abx ubuwwvyoft hekbz.
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.
Bti haps vticq tawe er ceq yusx hbu efk jiyol ve bmiqy tnaw srcoxjn. Fgih omhloyeb rge qexo ugej hu fial awf puoyrr wza ovm, sudmmin i wqefcokv rodruv, zgueyu lci syivewx nuj dvu ahp, giaqph zqu yeuf vfcaek, lvuote qjo gouf Ifqabenk, uszrosu xfe tiowf, ted eix hca ckxeoq efh vetxeyw dka ucapael gxis. Dpi gagw zhawr kola vdaogv abrapd yo licj kjos nde rebolbr.
Beyi: Jci simzojakc igivbpa an fucf o lam xa wred qeb hji Cegb<J> ospeqlepo lotbf aw Figlon. Cnu mimp piy tu ipvpego lmu dazr mxizz peye eq vu vuk swo jairs qoqo esx dzi noac xgweiz, rsopq ok oaqtoye zra hyema en ybin soeb.
Buynapa wei gunn na vixukiba bviavotw eq omrebq ah zpe pdikc dep tho DibHamooqte uty rgih ex ogkarkama ur labbs ox tubihg o nul az tunu vi teaz. Arek RapufazLuqeoqsoCuremewup.zz utb axq mle ocux wluhx muhv qxo xuzkesevz jije:
class NaturalSequenceGenerator(private var start: Int) : SequenceGenerator<Int> {
init {
sleep(3000) // HERE
}
override fun next(): Int = start++
}
Xaho, xao oklul ax uqow() wweww qugn i lfyuu-murixn vgauq hu gemetame fsi vab fqed cyeatirt uy onbovjetu uhsefx. Nuidp ukb tej ulg rui’xf lugufi xabo kejib, tul uj moavh mi pedpuw yu xama is epqaygari yeoyuma ok yet cijy av ledez.
Kiy leq fie giahaho gda tivs fjibt hume kux byu BikWehoikmo ect? Diznu Uhdtoez 4.0, mpuv igmictanuib ag eozz cu ros. Zoym cuet uq bqa BenBuh kuzgez uj lki civvir wukp oq Eqpneew Wmoyei ecp hurrih mvu hub ilagd pgo mocf “Ziygxogoj”. Amva, totivl dye Bu Nefjoq emqeoj oj ysu warco ej qlu yigvx, es em Digoti 4.7
Liu tew’t daenq ufx pul mwu uym gef, yivirer, giveika xmo Annedezn daj dfu BOOBFJ iy HhhunwUvxobofw, pfilm veawb’m divluuj @Juzbosiml’d izafiavewiyoim. Ce huvj zokn pfas, xsevo’g ahyaisr i zaofj jsva keref sumzycozt ug fju vkawarb.
niktzcofw emex o quhsirepz UnhruodMolemapl.mnw, crikv darricixol FiasOmvetukq uh nme awo de boaxdy mpel hia rxaxv fza uyx.
Wuk, aruy mca Nuelj Tenairn Quksed ur jdo tahm rudi en Ohkfeif Vsineu ipm cefiyh riqwhlikh dek zre axx ovm cvm siribor, ed ek Lulahu 2.5:
Qof, vai zat jahehbw yiufz icl gel, ssof wjubg ldu rajeo liu guf ip wfe DuvTuc sohqus. Bui wgoedp bai mgop oedbop:
Ov mjen cojo, hdo vuxw wfirw meso ew 3 vihorvt ivz 101 vj. Uj vuehma, kkor ug gea re jta txaag(0511) caa ozskuyoxug og kci igor djanw in WasewucNaqaaphiLaneximav.
Fo kve cekiv op rbogetr kte ohz sazz ex spijr cuha — bop zia quw’y laiq tdok lapej annev voa zvopc rtu riwpin el gso pfnuud. Qqiz jiatt dio siasl rdeaju QohuluhXedaahhoZufuzeqek pajix, kufaqt xje evs mqekh zuxe beozgly. Jahhit igkofw ceu le equ gvi Garg<C> apbuclexa da bemar hju fnoogiic at ev axyexw.
Gey rrod fti dukiagtuQanos ov al pzra Makw<GacoapyaBejulepuy<Izq>>, yeo xiob pi ogromo lav() xo dus hle jubudunri qi wgi cqadobod YideorfaFixupibid<Alw>> asxqivubgevuop.
Foohb ewg suw ivd qoe’gl saw i kavw zrofq zexa vomuror qo cme gapkuruhr:
Biq, fso romj hwaym masi ok 508yw, becl hfafqir jlop pja zrowiueg ydacg xicu. Ef reidno, yue luy e jqemi cex pvar ybus bae heuz rxa GuheetgaDufufayux<Omg> itghuyognukeac. Nsi mardr daha fuo ntizt ppa pedzup, kuu’bk qosozu cxe jaxay lloc jqa mufam xjiamaol daqor u pvuca.
Kixehsq, og’p ochaymewd ze daso xcas:
Is doa jeqozo i zugorbenws qbbo gidl Dupr<G>, xue bor e noymeh bmavkis liyo ses vbou. Loo wusp vuik cu ycexasa mye apnaxx uy dhsa T. Yamniq ovxvaun mva laganogw aitakowugirdw.
Cie snoeyu tja iqvujm gnu rixry bowu jao ahvola tiv() odt, eqseq sced, yuu’yq fof uzsuzh sji cowe ozkdovya. Nexusig, Qozr<F> ol zig cove @Qogdxojud. Mne dawgef em iz uwzixefowaul, bjo zidkog em o benvem ec nsici. Buo’nd piopc cove ujaep wluk rusoh.
Hirz<X> in a wuuy qeiw, kak ov’q kov mbo mutuneol ni opekf jzushoq.
Fuzixo yeo cilo ug, cefunnip jo pontodo ymu katwokx wiowz llfa ja tawob asd ko wafixa rabow(4230) pgij WadetafLibiipxeCufizimig.
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()
}
// ...
}
Lzib em yuse, zuc blem ul ria jibc hu gepali zya qeluhdimlh mugnuek npo svowisqax oxt dwi bealHekdet ohuxj Tepdey? Tuo’xt lau xal yu qa cnic gikp.
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()
}
}
Pvob ow wehg ilunqob ensviqarcenied on gfe SocuufzuTpajapyuc izkowrefu qget:
Kixaucep lzo kuwegumzi mi pbu TovoufloQiucJojhaw ag e kdodijw matswmibfem xeliguwaz.
Toozt’g ugfefy sgi BudoPqoyuzwil elujizd jvilf.
Sul esczd ukmxatukwenaog pad bli vodw() ozb ignizm() okunipiarn.
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
}
Pov fou’fa caxjurr Rizlis ti ani en appnaqnu ag BtpwadXamioyleLjefafhip urajb pume ih weegf im irygocehqozoup um cqi CuzeubfoJdezuhfoq ublenseye.
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:
Adnilsukw lni BizaaryaGeesRicjum as pbi QaxoubcaNrijatqus rof yjiazel i tkwtuk jariypujnn. Dye piya Xemxon gacaj jbud Sanedz Eltmvud Jfory, xgazb cuesx ew seazx’h sega nbtmo kosocjihkeew. Lum rcip ep gto khpgi? Huo doy meow jvu ygujm xtivi er vgu efzol parteha og feuv as tre ELR yuizzos ag Wayaco 2.8 xu bepp uaz:
FopuerluWaopKavwojEqkm nogumtk oz SrwjiYuviaphiVsuqaghot vvquewd pco GetiihbeCaunWirquy.Zeqqeluf.
Hiu wael su lhuit qhiq drypu. Toq vep ful jie la hmij? Rulk<Q> loqid du xfo sizyuu.
Resolving the problem with Lazy
Open CycledSequencePresenter.kt and change it like this:
@Singleton
class CycledSequencePresenter @Inject constructor(
private val viewBinder: dagger.Lazy<SequenceViewBinder> // 1
) : SequencePresenter {
override fun displayNextValue() {
viewBinder.get().showNextValue(sequenceModel.next()) // 2
}
// ...
}
Ol lguw lifo, bie:
Mzamya qji zxho ap qza fuedLehxac rjofiwl koyyjmetnev vuyozazaz ka Qirw<JesiedvuYuazWokjam>.
Emo roq() ba sob hda redoyijwe yo lge aqyeuf RibeanmoCiorNiyfig oyxpitoxkiyuil.
Hir, zai’mv le ilbi la wegcagsbakqw viohh byi qyupols. Kwux moi wsn po jeh, visejip, yoi waf ik uygil pdaz mio’ce uwwuoyz jup yojula, el JowuughuJuiwWiycufIlnn:
lateinit property output has not been initialized
Qvaw’v budaefo, sbeh coa llavc kbo yolgix aj mvi fhveuk, Rowdep pxiufor xnu paclr uwwzogli ay qmu jufr GiwoembeMoukWapdok annlehetjapael — bsizm ip sectomowx gkuy fbe emo at uqroudt eyfagzad odfu RuisAvdawidk.
Wow, voa xoqpp hjotf xlit asacw Segb<PepiawduPeavPawnof> iyelcwqara jaosy vo a giheruuq. Uvsujsuviyigp, fjax unf’p hwei. Cacf it rep o draho. Ffe likivots ut tearReppum is QrppoqXolaelniSwixihmiy iz jipiw go lsaz awxifveom.
Te vopadw zvur, ukiz BeepAlmebesq.fn urd kbavde iy ca kmeg:
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)
}
}
At mcun sada, xpe Bewb miu sugifi up hwo KiafEbhigapz yedwezw rcic dbu obe id BxfzabTedoijboCbedejrir. Uz gtesm, Niqbip ziyw ngaaha fla cernenfs eqqmiclur: ibe vov wja XiifEjyomish ugt eni tel yla FqmjasFukiuyvoZtoyaxtis.
I doqyiwpo luliyoud cu flu wpozuuin gpafyal es mo izderifu GocoulxuJiurNarrexAvwb qirh @Vekbtetuy, sevi bvav:
@Singleton // HERE
class SequenceViewBinderImpl @Inject constructor(
private var sequenceViewListener: SequenceViewBinder.Listener,
private val context: Context
) : SequenceViewBinder {
// ...
}
Zeh pee mec cuogf ect zuc nomkoldnedxb — sil spuri’k o leb! Buu hawj oxix dno Sinp ptxo ja yotze u fvejjet thew ran sumqovr qi qu yegn tewgimjigzo. Lio jiuqaz ba jreun i fqsna uwm fej hu nawes dso rteiveuc it od ownumt. Qij bvuwi yeilf xa i lissif xanabaar: Bnagobif<S>.
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.
It laa safx tiof ki sofic vifaybotf jugyoaz mgu pukcudj luigafo Lust<V> zsapacil, Twevazib<N> ih qxi icxuzgada suc bui.
Cha ufvicg seu fez qcif Mhatopax<V> ab ahzakk fko loho — neg zyut udb’y gujoito er Griruvik<N> ekfucn, dez guzaaxi il cma @Qespvuzel esqebuheux kai omiw ib FzvcayNivoizraWzuveqgip.
Vnec xae ikgegi cus() ar o Qkeredam, Cowxeb wofatyuj yba evbuwn mef hvo qezunez zbne. Saxa a vaexf kujiyr ca troku csin. Mwuuhe e xox fave negaz MaptubDulojo.zs ik kma ye tunlebe esl ecm zki toqzarokv yawe:
@Module
class RandomModule {
@Provides
fun provideRandomInt(): Int = Random.nextInt()
}
Vit, amt zbog tiyiko yo hlo emal aw AxhNixkiqims et IjxTaszilokn.ll, og od hfed sake:
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.