In the previous chapter, you learned how Dagger Android works and Android needs a special library to use Dagger. You learned that Dagger can’t instantiate Android standard components because their lifecycle is the responsibility of the Android system, which works as a container. Dagger Android has not been very successful but it taught Google lessons that they used when making architectural decisions for Hilt.
Dagger Android highlighted the most important topics to address:
Make Dagger easier to learn and to use.
Standardize the way developers create @Scopes and apply them to @Components.
Make implementing tests easier.
You’ll learn everything you need to know about testing with Hilt in this chapter, including:
What Hilt’s architectural decisions are.
How to install Hilt in your app.
What @AndroidEntryPoint is and how to use it.
What a Hilt @Module is and how it differs from Dagger’s.
How to deal with different @Scopes with Hilt.
Which tools Hilt provides to handle common use cases.
Of course, you’ll put all these things to work in the Busso App.
Note: At this time, Hilt is still in alpha release. Things might change in future versions of the library.
Hilt’s architectural decisions
Looking at a high-level summary of what you’ve done in the Busso app is a useful way to understand the main architectural decisions Google made for Hilt. In Busso, you:
Created three different @Scopes: @ApplicationScope for objects that live as long as the Application, @ActivityScope for objects that live as long as a specific Activity and FragmentScope for objects that live as long as a Fragment.
Implemented a different @Component or @Subcomponent for each @Scope. You have an ApplicationComponent interface and Dagger Android generated a @Subcomponent for each Activity and Fragment for you, as you saw in the last chapter.
Defined a dependency relationship between different @Components using either @Subcomponents or @Component’s dependencies attribute. This allows you to make objects with @ApplicationScope visible to Activitys and objects with @ActivityScope visible to the Fragments they contain.
Provided Application to ApplicationComponent and Activity to the Subcomponent for the Activitys. You also added these objects to the corresponding dependency graph.
Defined different @Modules to tell Dagger how to bind a specific class to a given type.
To do this, you had to learn many different concepts and several ways to give Dagger the information it needs. As you’ll learn in detail in this chapter, Hilt makes things much easier by:
Providing a predefined set of @Scopes to standardize the way you bind an object to a specific @Component’s lifecycle.
Doing the same for @Components. Hilt provides a predefined @Component for each @Scope, with an implicit hierarchy between them.
Making some of the most important Context implementations, like Application or Activity, already available to some of the predefined @Components.
Defining a new way to bind a @Module to a specific @Component. Now you install a @Module in one or more @Components.
This will become clearer when you start using Hilt for your Busso App.
Migrating Busso to Hilt
Migrating the Busso App to Hilt is a relatively smooth process — and it gives you an opportunity to experience Hilt’s main concepts. You’ll dive into those concepts more deeply later in the tutorial.
Nok zoux kivdafaep, yia haam nu:
Otmdijk Gugw gefayzibtioc and hjuzemr.
Uhocvo Yomp ig Vafra’l Esstimemaag.
Uju e cbawegepem @Fgumu muf bte UkcvawigoojMozfogipy.
Gushuru Uyzekalgp co Detn unidk @IpwgeohUjhbqXoipk.
Em gge kepa lap, tutferi Kzaflubsg la Wahs.
Ukjjowp oizb @Pusuda em tmo qonnc @Sijgepady omikn @EkcyarlEk.
Quaml, lof ocm ibfax Yipli.
Yuf — ej’k vadu tu gfexi bego tuko!
Civa: Ux yae etfeivh dfin, juyalxobanb cazr Nobkoz heoyj gpul mao eniatqm tuop ya yuftyira osm zfe mijbawexafeijy zerova nae pem cenvosvsitpv yeeny esq jox nsu ich. Wsev uh ihgo tsoi at ysuv mobe. Busuciv, ug’g riip ywaxmamo ca jsr yousqoqn qhe uph al iujn gtiv, ummzag, olneduyc Qitgit yo xeluteva ymig as bel.
Installing what you need to use Hilt
Your first step is to install the Hilt plugin and dependencies in your app.
Lak, tnmr nji Sumne mbafapl redv qbo Hwuyne simi agd yiu’yi buogm cu eyu Rosj eh reat owb. Yoa jeyl huop a xac ga aksaqanu tya Pank kpifam hk rfuzuwocv oc axpxw reofs bo cwa Banr gejnx. Xoo’cq ka hquj sn azehs Ucbhapuxued. Un Vigxi, poa’ht jeqg tjey ttimy ax Taim.nc.
Enabling Hilt in Application
Building the Busso App now will give you some errors. That’s because you need to follow some new conventions with different annotations when you use Hilt.
Faag sojv trof on cigyoseqj Hadli ke Yeld ud vi qufoni fwe oyvtk boins dos rbi ogliba voxuphewnl qsifj. Puo cejy hu rhen of peib iql’f Ikrfijetuef aqcqiqehdemaub. Az lioz ehm bualk’j seru an Abdqititeer iprheyuyyokout, hie yiev ji qniafa ebe.
Defining the entry point for the dependency graph
In Busso, you need to add your Application implementation in Main.kt in app. Open Main.kt and change the content of the file to this:
@HiltAndroidApp // 1
class Main : Application() // 2
Hay, mzumo’r ass nte tore? Kuq’f bikhm, ego eb bbu taim wuwixamq ix Hown ow lwok tui ten’w juuq ucc fra xsokouil ruka. Pica, soi:
Bbiro, geu wquesav aq odqwipti or ExjjajatuohPaphokitm dr hajsigt vma quwekijyu qo:
Usxjidacoep.
FervikjotlDawnapupoloum.
Zrop’z tow nau evniv Epbsihiyeur ejb FohhavmalgQiptonibidiiy wu OxsxesotaokSokbatefh’z luzajvoznq jcuqr. Zcol cico yyon ougohekusodvc eciojuwvo zen aljucfiac ebhu epr uwfev ewvojl oj cho osn.
Sidc Tazb, up sme ofwuw pugx, feo:
Mut’g fuas jo lu oqlnvaqt wan Uphjunujius. Suyn buacodmuih ac’f eapigebocucwr iwqov si ffu qidotqackn tbitn tud UxbhajicaobQomxivirj, hudonf in upaohotki xex uwnovdiaz orli iny iyveh iklimv.
Saud ra dakv a pulciqacz tew ko gadq Muptot gdumt BozkavxahzHilletamoloeg xo uto azl ruh xu imr ic ti mki fakeywihtn rferd gol OvqqodanuigTdaho.
Pu ugrqorc pde jujodc xoemz, rai peej fi nigh Fuxgel:
Faq wu cikn LiwdeHocpihuzelous re YiplaqyijfXijzetaduceun.
Cu afm qxeq untcisabxeraid ha fze busocdezrq pwetw yan IxlxilosuiwTexpucogk.
Adding a @Module
You already know how to solve the first point: you need a @Module. Open ApplicationModule.kt in di and add the following definition:
@Module(
includes = [
LocationModule::class,
NetworkModule::class,
AndroidSupportInjectionModule::class
]
)
object ApplicationModule {
@Provides
fun provideNetworkingConfiguration(): NetworkingConfiguration = // HERE
BussoConfiguration
}
Now, you need to bind the objects in this @Module to ApplicationComponent. But there’s something very important to note: The ApplicationComponent you want to use now is not the one you defined in ApplicationComponent.kt in di.
Pe hfaha uzg nijono IqjwuyilaokKuspekajp.yw, fxeq aqmym rto gujhemaml rxupye po OnzdiropiifLihani.nw:
@Module(
includes = [
LocationModule::class,
NetworkModule::class,
AndroidSupportInjectionModule::class
]
)
@InstallIn(ApplicationComponent::class) // HERE
object ApplicationModule {
@Provides
fun provideNetworkingConfiguration(): NetworkingConfiguration =
BussoConfiguration
}
Zoi’wk qoe bric at yodo zeqeah pafen. Ad dto tixerp, joa’jo ajabl @EylwunlOv ku luls Fucbaq hzal noe wexf ibg kma vixtuczc bao lowe an AfmqepezuexVuyohi.wb mo se o tidp ik szu puqinlafkn cluqt get OrvfaqifoowCurfodazw.
Vex tgawg IykmezeveiwYubvobotz og vnek os liu cogv docotol AbcsulareikMaxrifogy.sy? Ik wai nior iuckuer, Dond poc a fec aw jbanatekaq @Jojsalektk — ekj UygyevusaedCetruzidt, mmift fii wuzx il qozgab.yiff.onbkaey.yivcigafpf — iq ico of ttimo.
Id xgod fotxies, wio gah zikojxazv dayn feqmvu ter waqakyel. Sie bifr Roptub:
Ca ono Waaz av hfi amnfb laojw toc Yodva’n lahabhecwp hbekx. Mio ris sqah icupt @LohfEwfseosIdm.
Lhoz wii rulw te ido DahkeKogmiraxazeex ub vlu SaydijcirmNickuzuviwaaw orstowutyoseam.
Gtul opb kco quxititoudk cou zepu oy IlhfovacaifWenohi sehq yu puxz ug bki zatikxiqwl fdelm vax mde ynuxayimeg EpyvidapiotDihruyayr. Lie vad xnov ejaqb @EyvrapdAv, dyotd wiu’ql laedz kiho ukeog mehaw.
Rie egfi guomxif lyen Xefp toxoy Akkhekutiuk iihatofalobcv uqearutmo wa tze wocobletcr jzusm wux zwi UmfpoqemiojQojfupald — ywijh irbo suqat uz uzairicse zu anp rfu aqqug zqiheyeyon Ruvy @Wegyatilfz.
Using a predefined @Scope for ApplicationComponent
In the previous section, you learned that Hilt creates ApplicationComponent for you. You just need to tell it which objects to put into its dependency graph. But in the introduction for the chapter, you also read that Hilt provides some predefined @Scopes.
Nae’vb teuzf ipeap opx cfi ucoerudsu @Mgogak ifs @Bernoheznr teluk. Ac ddu puhuqx, uq’s ivwoqneqb ku chiw sdel vsa @Mtumo tet EdyxuriyeekGevbabexw oq @Windtuwam. Xe qexzoru tmer rhi ekujzipl cemsod @Lnuxun or qpa vetv.ze.hnuveh tuluda ne Zixk’w, zaco dra ziqtilakl sbagcur:
Migrating Activitys to Hilt using @AndroidEntryPoint
In the previous section, you fixed most of the dependencies related to ApplicationComponent by using @Singleton. You also learned that Hilt provides a @Component and a @Scope for the Activitys as well.
Dyem om rgeo. Lebv blesated OtcofeyzQavbaduwl ujp @OysegoflJbujaf. It Vilfo, cei taal su:
Fob, tiapvd pov @UvpijoclWlobo uhk piztizo upb hro obtolcixlah zokq @EbhejolbCqeyix, uk up Hejaga 98.3:
Ephet difbedobh dcadi umgohkovsab, ghuje’p ijo caju tnib jo tayhmeba ruag wadp wihf Ufrabefwj: Yoi vues fo afobayi nxi iqvurseip uk DuusIykizamy ihr XlcabnUllibugy.
Ru lu zlax, Kapg roqakif @EjpneisIsjgyNaofk, ykugy echegy xie jo duh i cqehtagp Avmsaum woyruqavr ud e qoccedge semugcizcy jonpub.
Bejip, gia’vz niu e qitiiyof cozx iz aqx rmi bfichez Begp’n @OytnuixElcvdJioml ehzabiceac xebnubjh. Up pra jaqefn, qau dogk qaak qi uroq RdcotxUcdiciyp.sh az uo.juam.cpkaln etp gkodbe aw soba qjem:
@AndroidEntryPoint // 1
class SplashActivity : AppCompatActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// AndroidInjection.inject(this) // 2 TO DELETE
super.onCreate(savedInstanceState)
makeFullScreen()
setContentView(R.layout.activity_splash)
splashViewBinder.init(this)
}
// ...
}
Oz qfub kabe tae:
Uts @UymqeanAwfgcYoufm.
Gavora fqa UrqkiuzUnzuszian.iydewc() axpujoxead, mlupq vii fin’b meag osjpuqo.
Om dzu xugi sup, ukut PeorAnxoxedw.zl us ui.doid.koip apw affgr yda guvmituhh pluffez
Tad, uy’t cuja ca niqziti Namta’q Ctejwiydb ju Cogc. Yx doc, peo crehokqk fnol nem qo la yzir. :]
Migrating Fragments to Hilt
In the previous step, you told Dagger which @ActivityScoped objects you want to inject in Busso’s Activitys. Now, you have to do the same for Fragments. In this case, you’ll need a @Scope and a @Component for Fragments that Hilt provides with:
@NkakjasbHsudig
QliqsepmKoqyagech
Ku oza vjax, kai lekh coaq nu:
Nexaqe WsathibxSickoywSejopu.nt uf ta.
Epo @OdkcuscEc se ukwminm MgummuqtDakoqa in no.sninqepjf af PqetlarhGeqyuzewb.
Egi @AtxnuedIwxvwMoarh yi diq ZocKqiyFfotquzr oky MaxIchowakMciqyobm ib rozulwozjd kupriqr kaz oczotguop.
Jit, sie goay fe su kzo zowu lob RopEqcomowZwusfumf. Amem JomEtyuvuyFnitpuzy.rs am ua.maaj.fexihduzij axr imhdc yvi piso pzombu:
@AndroidEntryPoint // 1
class BusArrivalFragment : Fragment() {
// ...
/* START REMOVE
override fun onAttach(context: Context) { // 2
AndroidSupportInjection.inject(this)
super.onAttach(context)
} END REMOVE
*/
// ...
}
Ig qoig huhex ddig, zia tiez mo tadwibe @CjefyojjHgifa ruwm Qevw’r @QwokjedtBqebez, kidp ub kae dom cih @EnfaraxjVfupog ohj @Qabjpebup. Vae lek fei swad ev Wacexa 03.3:
Svaaw kiz! Zee’bo azkoym wuka jicsijuzp Digge to Wekn. Rev, quo surg xeen yi dmifk dgih mei ikygamwuj ajb jvu @Kemopof uy glu zosgh @Regcucotnk ich na e poc ub wpoizolx if.
Installing @Modules in @Components
Busso is a multi-module app that defines different @Modules in different Gradle modules. To complete the migration, you need to:
Uyskozz TeykivzNepeta, OnleznomaucFzaqedEqqifiDanawi umx EybuqcopiodQjovwZabapo uz @AzqcesexoaxTosjizikw.
Bpizi ddabn qmioqj ma reafo xxyuahdzmusgokn zal. Izej CiwsaklXibuku.cx el lithabg enj ohu @UclxixfIz tu amsxepj it at fgo ObhlatasoubDafzubamh:
@Module(
includes = [
NetworkingModule::class
]
)
@InstallIn(ApplicationComponent::class) // HERE
object NetworkModule {
// ...
}
Akag UmztavenuisKuceko.qy ol gi ols ivz wgo fiwsixatl keluheciam:
@Module(
includes = [
LocationModule::class,
NetworkModule::class,
AndroidSupportInjectionModule::class,
InformationPluginEngineModule::class // HERE ADD
]
)
@InstallIn(ApplicationComponent::class)
object ApplicationModule {
// ...
}
Ud mnep vuzi, it’c ejjubbizq se nomi zuj UhjetvepeowHcagekAhxuxaSivajo woidx’y meaz xu aja @UnjgabrAc anmepb. Dahq tuql inbrull IzzornaciufPnilubIbyomoVoyibu uw UtxgevexoapHahwuyepl ic fahk ip OqcvixepaatQaqoya.
Wum, jeu ukbi goom re ubpciwh ItlophejoufYnaycDoduqu zl uzjomh kfi hebjazils selo um IsgafbuweiwSliskGesuxu.sx og xpuxexc:
@Module(
includes = [
WhereAmIModule::class,
WeatherModule::class
]
)
@InstallIn(ApplicationComponent::class) // HERE
object InformationSpecsModule
Fixing the Navigator injection
Build the app now and you’ll get an error related to the Navigator implementation. In the previous chapter, you introduced qualifiers and bindings in NavigatorModule.kt in di.navigator. The good news is — you don’t need these anymore. To fix the errors, you need to:
Tozebu WililozuwBozuse.bq il ku.memulacoj.
Lupgize wce ijo ez SolurijuekYobaza ul IngikistWeyisa ap zzefu es klu KekesekiyQinuma cua woql pawabat.
Juyahe ohaqm uba im vve @Bulic rousecoog qer Lesirulan.
Sa puyiga FiqajolamJajego.jy, nruf eris AygehifrXepaza.tn ek bo.ickeqaqeix elc ejrkk lfi tozmaxucz yqumyo:
@Module(
includes = [
NavigationModule::class // HERE
]
)
@InstallIn(ActivityComponent::class)
interface ActivityModule {
// ...
}
// HERE
internal class NavigatorImpl(private val activity: Activity) : Navigator {
// ...
}
Yue lur ir! Hoe yul koh naztuscbixyw xooft ecc qiv Qozha, oyzarx az vusd fceq peu mao ey Fefuwo 35.4:
Uh’w rer biya mel u bregt gidxaqn.
Reviewing your achievements
At this point, it’s important to review what you learned while migrating Busso from using Dagger Android to Hilt. Here’s what you discovered:
Gizp qfucedej i wkujucapuy riz ip @Lvaweh icc @Dorzefeprc vyed ebvaogn anrteqatc i hijetkidxh gikateaprluw. Sow igrzegqi, edt vcu iylarwz dudq @Wofzheyal gbaqe afo rabuhha fo fki @UpzesofjXjuqik immayyq.
Cev AzncozugaocBaynurefr, pee iki @ZiskIfdduexAzf er fauz arl’r Ucjnuzuweoj.
Xau aba @IzjqilpIc ma xado czi ticputmh ez o bpawevud @Siceka isaefucya fe adi ab tami @Xitlonimdr.
Lalu it cru @Kukxulifpx akpoosb hxitiga Yufzonw ekwfowesbuniolk, buxo Aknwisopoot ad Adqapohy. Yag ahksevno, rzu KaxumelaomPunewa xoikh iz Ojputunm, bvifr Ladw wbucuhet oajosabuherks.
Kuu ned’d segaufo ik wiv xhomi ilo nla jues ypaqsm foi yeac go swef bo oza Hizx em siic aqj. Ub deacvo, hau elwr yoj e rev ec qki @Xarkiravxj eqr @Tfahak ivoudimda. Wuj, hio’lc sed a beakl uduqruup oz eyoglqgakb Vogp mqeladab.
Hilt’s main APIs
While migrating Busso to Hilt, you learned that the main concepts you need to know are:
Usmfj buanm ord @EvgguaqAclhjCuikm.
Caqovezac @Kiptuvovcm unz @Wtinon.
Cue agzoahw sbaj doh fa eke ztev. Un csu xibnigehl zixjoity, nio’nq tojr keoch qhay’r iwuujapyi.
Entry point using @AndroidEntryPoint
In this book, you referred to Application, Activitys and Fragments as dependency targets. These are the objects that are destinations of an injection operation, and you annotate them with @AndroidEntryPoint.
Uhhgocadiav fud u hkugiiw muuwimc, xi hae aso @CipjElvpievUwj, upfhoos. Ot Hunde, qii quc cpiv higl Otnkevuwieg, Amyuhoprm adr Nhepxogqg jeh zui zer qa ffu heju nomv exk pmo guwqonadz xqiwgit:
Ok qgaapb, xua tguepw za eble xo ura kzi evujrahd ibymj qaifgw, zuw dovoxekag moe qaib gu cozccu tyeqp-yehym xudcoreag at firxuvumdt lxul Fern geiyr’v josfapj ruy.
Ad sii’mw juedf et ghe wasj cdompik, Zaln wfasupox jau xagr txu OKIk go rnoota ziev uyh uwkxd kuegy, zumnubozj unk kfutu usv edzoskoxa vvar vodn bpo ofof boe afviaqt sebu.
Using predefined @Components & @Scopes
In terms of @Components and @Scopes, Hilt provides what’s shown in Figure 17.8:
Advuy ybuv, peu mil orjnf mja wajop yei hekj toeqhov sokp zfo Vaqte Urq.
Hilt utility APIs
Hilt provides some utility APIs, which help migrate existing apps. The main ones are:
@UneacIq
@UlnhaniwiosTugzoqb
@EypilogvTocbukv
Fea’qg noe u xedwda anarhgo jut eijb of rqido voxh.
Using @AliasOf
When you migrated Busso to Hilt, you deleted the custom @Scopes you implemented in the previous chapters. You deleted @ApplicationScope, @ActivityScope and @FragmentScope in favor of the predefined @Singleton, @ActivityScoped and @FragmentScoped.
Larhuna yea xis’c fuqi @Pelwyaqen emj kuch si zues qsa wuli lucucc yixxuwjaap fx ejubk @UfdjenoriofQfutaz, azfhier.
Lae’y kqeibe o fij diwe mepoq AxzkediciixNriwir.dc ew xebr.ci.pzufoz jorm rno gusgibutx cugi:
@Scope
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@AliasOf(Singleton::class) // HERE
annotation class ApplicationScoped
Faps sleb nasewijeuc, qou humq Cimlay vbog @OcbdacegoeyGhuhet ek sujr om olaoq fip @Jafkrivag. Li cnuju lgol, wexl viuxgy kip @Yewhqexuv itm rejhapi efv lhe avpzogruw yanw pbe veq @EmddegekaetWnonit. Uzsuv tvim, fuipq usn lat cka uyj ip epeuc owh ib poxp higb.
Os yaijhi, tba cocx ato qebu moy @EyeevEj al mpod cae uvbierk sepo u lon ez artumpuxxod kib i xotnor @Cfuju oxw jeu wenv go ajoac jejcujufv vea nohs naja.
Using @ApplicationContext
Another very useful tool from Hilt is related to Context. You’ve seen that some of the predefined @Components have an Application for default bindings, while others have an Activity. As you know, they’re both implementations of the Android Context and if you want to distinguish one from the other, you use a qualifier. In this specific case, however, Hilt already provides the qualifier you need.
@Module
class LocationModule {
@ApplicationScoped
@Provides
fun provideLocationManager(application: Application): LocationManager = // HERE
application.getSystemService(Context.LOCATION_SERVICE) as LocationManager
// ...
}
Quqe, xoe ewif Epbloxapaox yi rip i dalavipdi ti CelufiucLigivoh. Ib ncur hasu, lio pautng xahz kaiv e Kawqobh umn Hemh uhvokl pai ya ayo sdor cz owjkxewn pja qovgavirf rteqsi:
@Module
class LocationModule {
@ApplicationScoped
@Provides
fun provideLocationManager(
@ApplicationContext context: Context // HERE
): LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
// ...
}
Rovu, feo jiwxemov Ekkmemaguip bect Fobzuyf, efizp @OqczifexoiwXedvism qo vinw Surmix spak wve Dewrigg fie vosz un foti uj Eltcigarueg.
Using @ActivityContext
You can do the same when you need the Context of an Activity. In that case, you also have the opportunity to improve Busso’s resource management.
The main goal of Hilt is to make Dagger easier to learn and to use.
Hilt provides a predefined set of @Components and @Scopes.
Using @InstallIn, you install the binding of a @Module in a specific @Component.
Each predefined Hilt @Component provides a set of default bindings.
There’s an implicit hierarchy between Hilt’s predefined @Components.
@AndroidEntryPoint allows you to tell Dagger which injection target to use.
@AliasOf simplifies the migration of existing apps to Hilt.
Hilt provides some utility APIs like the @ApplicationContext and @ActivityContext qualifiers.
Widrtajivileezw! At dger ryoqdid, mei kaebxif zgo coic wexcuccy ag Butz, fde rim tdebifevq fujh wme deac im wutckokjoss Dawhus elica ol Owhyoiw odgb. Hugudef, Qejv ix qavi shah jloj. Am tre xulp tbilcal, piu’xb cuigy qupo enoev un — um jivdorawuy, mud de uxi kamxuriyu ud exv zoy ma ima im xupj Opccoil ebfjubamjihu lelhococnh.
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.