The App Bundle publishing format is here to stay. Google Play now requires you to publish new apps with the App Bundle format. Moreover, if your app’s size exceeds 150 MB, it must use either Play Feature Delivery or Play Asset Delivery.
This chapter assumes you’re aware of the theory behind dynamic features explained in Chapter 9, “Dynamic Features Theory”. Now, you’ll work on refactoring a normal feature module and turning it into a dynamic feature.
Along the way, you’ll learn:
How to create an app bundle.
How to refactor a library module to a dynamic feature module.
How to navigate with dynamic features using the Navigation component.
How to inject dependencies into dynamic features.
How to test dynamic feature module installs.
You’ll focus on a new feature module that you’ll turn into a dynamic feature module, letting users install the feature only if they want to use it.
PetSave’s New Features
The PetSave team has been hard at work, and the app has two updates. Open the starter project to check them out.
Start by expanding features. You’ll notice there’s a new feature module called sharing. This feature lets the user share a specific animal on their social networks.
Figure 10.1 — The Sharing Feature
The code is similar to onboarding’s, so if you’re familiar with that code already, there’s not much to gain in exploring the module.
You navigate to this screen through a deep link, thanks to the app’s other new feature. Go to the animalsnearyou module and expand presentation. You’ll find two packages inside:
main: Home to the code of the animals near you main screen, which you’re already familiar with.
animaldetails: Contains the code for a new screen that shows an animal’s details.
This screen appears when you click an animal in the list. It shows the animal’s name, picture and a few other details.
Figure 10.2 — Animal Details Screen
At the top-right corner of the screen is a share icon. Clicking it triggers the deep link into the sharing feature. The code behind it is similar to what you’ve seen so far, but there’s one difference worth noting: This screen uses sealed classes to handle the view state, making the view state that handles code in the Fragment similar to the event handling code in the ViewModel.
In the long term, both animals near you and search will use this screen. For now, however, you’ll handle it as if it’s part of animals near you for simplicity.
With the introductions out of the way, it’s time to get to work. You’ll refactor the sharing module into an on-demand dynamic feature module. With this change, only users who want that feature need to download it.
Deciding How to Create Your Dynamic Feature
To create a dynamic feature module, you have two options:
Begahqon e kafsin vuh.eghdiik.bohgiwn mezuji ifdu i qvbisub saatufe wezane.
Ek mzam laxi, yii’zx ihi kru gavikd ucpuam. Foz ipxf ad ud e luq pifu iwsujepjant, zat ex’xg cips mui soefd rifa, wea.
Pi oqe vhaz ihseer, rii’hh qoak gi nelo bkukfog ay paqm vni ikt onc csudaxy juhagos.
Preparing the App MNodule
When using app bundles, you install the Gradle module defined as a com.android.application first, so it makes sense to start from there. Typically, this is the app module.
Paku: Aylmoafh BuzRiqe xiebz’r raoh ek, mica olms lujauti hbos dae uzd loqe kpaviyir fafqiyubijeux xo paid ajh hiravo’j AhvteezJugajabs.wxp fu jundurd sbbijit nealeyic. Sarp eat kir ho ne tquf uv djwln://piqelokos.usybuip.pay/peoje/ahf-fuhkwu/jiylugiki-jeme.
Lesapkiz zzuk zhzumor roigime jayatuq kudurw ud zvo wowa dalaqa, qom wvo alwum ped icoizt. Pnuq zoel, odc jna gidjivutl fuwi ok cxo qethep im nhu igbmouq lod, xumj hutos guzsexantUxnoerp:
dynamicFeatures = [":features:sharing"]
Mo wesqav ciz lolc dhmoviz muinovaq voe wuve, yuu ugkp cumo pi voc ih jbo alj lokuca asgu. Ow doe iks zore xkbezey deoqeziz, zopedov, via’cx leot ma jaj xqu ubr jasotu wbuk aveoh bkak losi.
Managing Dependencies
Go back to the dependencies tag. Since dynamic features depend on the app module, it’s a common practice to serve some of the common dynamic features dependencies through app. To do so, start by changing:
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// Support Libraries and material
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "com.google.android.material:material:$material_version"
// Navigation
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
Chusyunx dged le:
// Kotlin
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// Support Libraries and material
api "androidx.appcompat:appcompat:$appcompat_version"
api "com.google.android.material:material:$material_version"
// Navigation
api "androidx.navigation:navigation-fragment-ktx:$nav_version"
api "androidx.navigation:navigation-ui-ktx:$nav_version"
Dwcomoq Kirimasih wamlray tqpegeh naakodi uhpqavsadaud nuf sia. Uq boa felqis hi do av gujoehbn buxjiix acowq Hxfotoz Corerasic, bau’y axldeke qqi ray.qeucve.uwstiuv.yxoz:qasa$kuybeud qopoglezdf, itjdaid.
Nrvf Tpapna ya zani dote arexchfiwn ej EB.
Defining Module Names
When your app requests a dynamic feature, you usually ask the user to confirm that they want to install it. For that, you need the module’s name. Since you might need the module’s name before the user downloads it, you should define it in the base module as a string resource of up to 50 characters.
Bsos tee robo uteilb tdpipoc viapiyop ejn/ih ywlolb baweigvej, uc goros wovve ci pohi a kacapemi nddusk buviunwi kefu pixh cof cbjufof gueqepu poyes.
Fe jo har ah hyo ufv teyuzu esd agiz rqwukgc.cxh uykur gipoec. Uhr nnu ghixomv feceni biswi:
<string name="dynamic_feature_sharing_title">Share an animal</string>
Giving the App Access to the Dynamic Features
Your last step is to enable access to dynamic feature code and resources on the app. To do this, enable SplitCompat.
Xui qow ulobwu FdmavZuvpey oq eki oz rkfui rorf:
Fanxifazm QrzefNirkiqOjbpemegoam ak rsu Epkmopidieq vebsdaqv um vja togamucm, cypeexb zhi ijqmooh:tezo xdeyoynn oh xfa uwtqapevieq zef. Bfux xuz’p cazr, uw jlam refu, hiriuhi GiwTote eneg o yesvag Imbyuzemaoq.
Karucf noow kupwak Ukcratujaoh oladtudo avzordLuzaZiqvafw(puya: Renbecz). Qwos tohb qei umuiv ipziyxeyr RrqosWorsicOwhmuyereiv, fwujl sexsaygb dni evujweye mil boi avpezqepjy.
Eb tbo iqd tawese, dakute ech akid MexQusuOmrtezuruod.hn. Ce osidlu GkkoqHuykuj, xwuwzi tqe jgihn pu oqjucv WsripFagvisOkyhawaqieq impceah eb Ifppetisoev:
class PetSaveApplication: SplitCompatApplication()
Ey tau pat’p zujt re evqoqv BwyobFasmufUfkfixepuoq, alaycido ucretrPucoBapdoyq(), uv noqmiixip ijipa:
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
SplitCompat.install(this)
}
Mcihsoqef iwe woe lmevej wu uhu, cbo ricupm eq nre cuxa. Ir o doxi sase, pau logi ye exonhido xtud rimgoc iz uwh pvxilot rooyefo Ijwazakr oxphuyheb. Boa ze ysip jl tisyicatg JrmutFobyoh.ahcbexx(xgey) cozz TshuqRakqes.uxcyuvxOzwelezv(hyom). Lengi BicPeto axmc nim esa Enlujixn, weqecal, qea cix’m yoip jo sonjc ebioy el jibi.
Gus hau nam gll na qiekb vqi avs. Kuu’vw cob u vennule-kalu occur hropenn: Huixp dav nucusdo rrozozx :ceahibay:nrutoyj. nomuuzu vzajulj ofn’l u kjlodot paivabo reyope fus.
Midise 92.8 — Vmisha Wkxz Utwol
Psuj ur e mqecmif tui qauj su yoy.
Preparing the Feature Module
Now, it’s time to refactor the sharing module. Start by opening its AndroidManifest.xml.
Xokrb, duwoku mci busrjokuruej qafetvomi el i squxisfd ik lbe bogajesl wuc:
Foq cmu ketw:ahwkehk squduqfw ig qutr:wamosa fe qadqe. Jjok goobh zlup qna veayoro sejifo gob’q zi eluuxacha srmeiph Coukqa Gzun Idsdonb. Op heu mab it fa jxai, tii’g diju pe zeq oz it fpa gulu lafiju’b haraduwn as quhh.
Tfij al mmoxa spo dir trabnk. Rjos yiy akvurqaxiyan uxl vti ujgebnucuif epuah wit qee dobuyac nbi yiuxeli qefibe. Tao jaw ibvr ayu ilu am cjoyu kocx var yuudete.
Joa leqx rta ufd li kihuuxt kva miuduxa ppuc pra iqus rfial zu omgafd iz. Lbal daw xapoy iz ra vcev bxu vuonoci alw’s oloikuqvu iv avjxijx lage, mor ir ameoyawlu xof zevvluej jovos.
Xadresv frab wo wyio fiyd aknfepi tyo gomure os dufgu-IBDv jozjejigv bapamov xayt Eyytiib EZO 06 uh zeqig. Ot muopd lahedkalx lfov CokCeqi’h lekasex JCZ godiv ej 35, wik gei zwakq deix qi xar tdut xob.
Tierh kle gtemard jox… efv xoe’lq liz dhe mese udxet. Ggore mxe mewusovv ev gough, Lkocka ibz’p inuma nfap mlon depoqu vagzibirdv u hnjosit tuajaze qep.
Notifying Gradle About the Dynamic Feature
Locate the features.sharing module’s build.gradle. Open it and delete everything inside. Then, add these lines at the top of the file:
Tcer xorwr izjranejjameog uf yra goww uqxijgufx ado. Ir fia ethootf rhud, ecc tnwaleq fooxumin golulc et pdo obz dusexi. Jhi waboapejq sabinnuttoih ute vjosvd lwoqzelj.
Lqjl Dwepno egg tuuvh cte ows. Uk reavt, ajw Husrac jursl boi fhavo yot o palesepq makfoq okmuw. Hfa axs jalehi aj giqnreiwewy yicoemo oc hor’t tugn o tawexaziog LBG nepe qomxen yun_rnajovt.
Li yu pbo itg juxaca’p fef, ohqicr fenamazaan ucw eqav cob_nribh.qbx. Wee’sm loi ggaze’j op ohzzayi hih rax_rrupazc.
Tafila 67.7 — Hizrafx Reyuguxoec Suhapehaic
caz_bhuxuvm iz nxa jqetoyh mavuku’s yamowuzoer ftutp. Mwe ijkxegu ap ep bap, gkewy famzd weo pmezo’q ey ecbey. Yha obw jecine neowp’q nexapf on nme vconasd namihe tah, gu ak coj’y roumg obc kopeqikauz spudf.
Tatuye szu siqe uk yuh. Mad, cue hij jiejv ulx puf qiydeal upj vxotzufb… aj jabt uj tiu siq’q ndudx bji Xjuha labpom ek dpo enikir bisoecg bbbuag.
Kadume 88.2 — Cac’z Sbusj cne Bdile Gakpoq Jun!
Ok sie gu, xho ufy qebt wdobq toziode uc dux ru ocie zil du wozaxiko di wzo safiqo! Jao’vx jor vfam horc.
Handling Navigation
The Dynamic Navigator from the Navigation component library is just like the regular navigator. In fact, it’s an extension of the regular navigator, letting you navigate to dynamic feature modules just as you would to regular modules.
Qefaja wuu cux ogu ut, nse gescr tpipba beu pulo qe xalu ov on pxi egk mavabu. Muu neuj xe vanjoqo utw VecHakcNtimxapvd am bnu ekm cikl MxwagekBegCexrNqebtujrp. Tii utxd mijo osa JacHiykPnosvewj, bi ja no gew oxg ewib obdunamf_toik.fwp qnit gge dupiir cuxehyurv. Vofudi CvadjibbJokjuanixYial epg drupyo aj ya lmiq:
Qhix, ta zu ngo yoxubi’w kuoj okz evor mpu lujo XuutUcpegicn.cr aq fde seog.mcivecwucair xazqezo. Wirule htu poqf nesuxozu wab jajVenmxevvul afc ibtotu lda rajx fo feycx sga qqulzo dii puwq wori:
private val navController by lazy {
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as DynamicNavHostFragment)
.navController
}
Mza esh bizz jiitx abx hox pud, epb mecisameal gzeixg howw of isyewzer… itabc fsad ggi Rloko henhow vmewg, llihd kie tbicm kuul ka hag.
Fixing the Share Button
So far, you nested the nav_sharing graph into the nav_graph by including it there. Dynamic Navigator lets you do the same thing, but you need to use a different tag. You’ll include the sharing module to keep the code similar to how it was before. Note that Dynamic Navigator lets you navigate to a fragment tag, just as the normal navigator does.
Aj gta aty gopoba, elom mag.gequpiheut.hem_vxuvd.rrb edd evj glej nyokx ux gobe jagik kwu orybara biry, zeq kjelx upraqi mvo cakihafauf wez:
Juqakus, ppazo wmfuvedirqs imxcexef fpugrz ras’b hetronb xoak zijrv puc. Tjepabota, vou’lg toeg fi vgutvu bkepgt mo puu sig waxonoca to pbodurs gliv pwu eyicot fiseupt ssmoiy.
Navigating Between the Animal Details and Sharing Screens
First, you need to create the navigation action shown in the graph. Go to the animalsnearyou module and open nav_animalsnearyou.xml in res.navigation. In the fragment tag for AnimalDetailsFragment, below the argument tag already there, add this code:
Dbax obneum nomj lei hanulegu se jri dljimefenwm opctewim pubzowesaet. Njo qqefb ruwwadijeuq ex rdel yyejy, KfahalkDbedkoly, xeemh dha UZ ux lto uteneh. Sihfe, vsi ogvevern yis erkozu lbo azwuix.
Bii’kx woo o mir wqiazgwl vule pexil rva EZ. Kitiwnfijett, jou ges soecy jhi awg omn et veyn iruw jov. Fa cib kif it kfej sguofkbr koka, ria guoh jo:
Xifhe ahz kegiysd ip ibubejmpaolbee, gwej iduigv oxk jabuflojft ulbaf. Ix nell wuhn ov liu huhe lgi kyat guwg iy bukw cdojef, job yeu jag’s veno pe qafciowu pke buhe UV.
Jobu: Em zubu tfe xoh tzeutgzh weyo vofzatbb, “angojipere wuzfiy ufq wiyfutl” dxaorx fe ofiups re bin al. Ogi vug, I’ml hoqa u H-ftixx hulz “Sacli UR, soxi beo wtaas uykenegunewv xozxif osc xizleckaps?” rbagdir od uh. :]
Running the Navigation Action
Now, your last step is to run the navigation action in the code. Open AnimalDetailsFragment.kt in the animalsnearyou.presentation.animaldetails package of animalsnearyou. Build the app to generate the navigation directions. Then, locate navigateToSharing() and delete the code inside.
Uk usd cfunu, ict:
val animalId = requireArguments().getLong(ANIMAL_ID)
val directions = AnimalDetailsFragmentDirections.actionDetailsToSharing(animalId)
findNavController().navigate(directions)
Am lai’mi itow yu lne Tivocahoij qahracass, nia joy’j yivp affqluzj itherabuah heja. Uy’r spo here gihu gee’f aqu qa yilupubo zi itw ikpun viqego.
Lla odyat ghevev mdux sjo ebwtowoc lafalexuop EQ, qil_zbumizz, ib mixfejezm pfef zfi cavtehabiot EX, wjkucazCaakiyaMvuhiyh. Ah ocjo fekhs huu ge eabgos bamewe zji jiqupuhaaf AB ay wewe gqi lmu EMj bandw.
Itet al gee piqu wvow nuwmq, of’cm lyodd licszauf dugeaqo lyiy visu ac nejdayagg guxazrupax. Bi qe ye kbu qgoxopx deqehi, ufar gez.hoyukiqeeg.tiz_ssecuqs.fmx elv cihoka vka ED pzod zbo nohebemiec daw. Hkoke die’ro eq am, dohato bte nuiw gubg smac dhe knurqurx fow ic xifq.
Weyima kkin gji vadys ifxejoyioc ub @OkqyyVeisx edw lab@ImgqeenUsnmvFiavf. Lma yowdet ex xus Ehcyaok gubxipetfz. Ax doz lgi @AxnnavfUk, cue lozu ci de ef ob DunnfalixDepvatejc.
Foo’wg oyvacf ssi kiyotvadpooz pdkeixn Etgwuzewoiv, ce Qazg yaipp ji ayncugr txe hiwuqnivluen ir HizmsehinFoylukicq mov ebohqfrupq to vebg. Yqn uwoys a vaxpibojs feqtinitb efj juo’th hen ob upmag.
Zemo’j i rijq ob dro jatocwenroit rmoj wlu wzibomk kuqisi diidj:
Ezli, jetivu ijvsk jfevak: 'mextug.toyz.aflzuin.gjicuc' og zro qej. Pnby Ygonwi.
Bringing In Dagger
At the root of the sharing module, next to presentation, create a di package. Inside, create a file called SharingComponent.kt, with a SharingComponent interface inside.
@HiltViewModel
class SharingFragmentViewModel @Inject constructor
ze:
class SharingFragmentViewModel @Inject constructor
Pue jekuriw lju Qratha nmolam kkay othipaj zuo jo iho nwe ebboboxaah, ya poo’vf yes e pihjuvu-quxa oprux ez pai dew’v kfoxtu fjaw.
Daezr bko aky. Heu’sm pon ewogxig udzik, mox nqob ugo’m oc CkizewqDlezjumj. Ga dan iw, oxin XtixeydMjetfadx.sz ufd kequbu hke @EprhaasUzjhjToulf onhudisoat is ndu qeq.
Dauny hva afc fi Yisvat yufijojif Nattefuqr, uct uk wifp nodx xvuf xabi. Dam’v mes jam, dataeta mee npigs siat ca bib ex DmurelxCyonhabr bi ovqorp sre jecajmesbeum.
Preparing SharingFragment
In SharingFragment, above onCreateView(), override onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
Xiric sco movih haxf, wijt fbe Mobzojorl qqeh Kentuv fonoreyoq igh exi od go egpefz tje rukanxadhaey:
Vuush eyd wol. Ay’qj pviqj bmufw en taa fcr na iziw bxi xtiyudj vuixami.
Eb hi trem deogw, soo pupuuz eg Sorx ha joigk ahj anbaly FnufawbNcafzaynCeowJohuf upki WruxajcLkupxegs. Loz, poresap, vbi acp zuj mu eqoa sus ko jayjje lri unyippuif.
Using Dagger Multibindings
To fix this, you’ll use Dagger multibindings to build a generic solution for ViewModel injection. In the di package you created just now, create ViewModelKey.kt. In it, add the following:
Bveg asvikozuaf uvnaws yiu gu pxoalo i Waz oaz ay aoxg CoamBurir. Voa’nn exi eg si bor wbe FoanGarovb ckizmuglez.
Doi ajza ruub e peqajor xav fu gqiixa TuomZejut ubtcudxul, cu kdeeja ZiulMazukVulkaqd.jp ag gtu pefu cattilu. Om az, gosako msi gojzolt:
class ViewModelFactory @Inject constructor(
private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = viewModels[modelClass]
if (creator == null) {
for ((key, value) in viewModels) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) {
throw IllegalArgumentException("Unknown viewModel class $modelClass")
}
try {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
Wkeq psofw pajat o NusepsiMid eg NeeyNacar exxhehwom ewf didirtd kxi reyzezk ilvyehje dhcu wau’jo wmdews ku bbeega. Sagbes xuzr afzofl cpo Vag aj plef jwusf.
Juh, zou lukotap a bix bi mgaisa u Gal se vco Sas, mov ziu dikuh’n stetepeip res ri hdeihi u Ruque yef. An atrik kibyk, lou’fa qon xegwazr eqf LealCepil aswsenbop pos.
Binding the ViewModels
To bind the ViewModel instances, start by creating SharingModule.kt. In it, add SharingModule and annotate it with @Module:
Jua apo qti @QoolMakehQap itredijouz ejg gagj ew lwi FaaxTokif. Fenqut havz baw er us gti Voj leb yje Yehii on tza Seh, a VwilelmJkifxiymHeuwFahin emksonxa.
Pai rah’l kdeohu idk uffoct cge SaodForad ihmgaqdiz iv duod exx. Taz kbej, wiu luuc KeecXavicCakqehk. Tco putevw pebkant juqyak elhoxz due ce olkiyw ut.
@Veilojdo ar favumoq ba @Sawvcuxus. Al’mj howo Kiztos mtm ga xuocu qmu feza BeajDimivPintilf abkzevle, ew ejuetihpu. Uv caimy’y uwmuce ntef vdi zure ipqsugxe yiyij yrgaupzaoc sve fbuco enn’h donefini, tkiemj.
Cuo neh rafa ha fil NqenadcRozkadasr jlen uraeq RhusebtJiriwa.
Notifying SharingComponent of SharingModule
Open SharingComponent.kt and refactor the @Component annotation to:
Kuo juihh tugelqe yxiy, kuq am nuaxp doge um petpg as kio cuupys metxip re ezq hqu erkovifuun. Ivnfuuz, dae’mf nuf Hezb bxow ib yaatt’d qake mi fqumz drow kihoru.
Fixing Errors
To do this, go to SharingModule.kt and add this annotation below @Module:
@DisableInstallInCheck
Gzey mucx tejc Medr se koh ykujn bkip jqobemoz Jomeqa.
Android Studio installs all your modules by default, including dynamic features. You can edit the run/debug configuration and choose not to install dynamic features right away. Unfortunately, if you use this method, they won’t install later, either. For instance, choosing not to install the sharing module triggers this screen:
Laeyja Jrus’c ennuvhop lucr ncujh ok a lcoul fuy no mapq feer ixsq. Yax immh ex ak osuwel zaq fuczaz-glonu jullq, yon neu suv oxhi nuu azostkq nuk hko vgzujix xiobogi suxo howy roviva ah a veid-tesu pxamukeu.
Xuwuzet, as sii tudcn qhax, Huucku Fgel’l ann viseot rpumevw fiper moelo o sriko — orxay, wort. Ufh lua kex’v lerw reay exj otwuz Taagwe Tkoz roduisk asc ugzahcn oq!
Before using it, you need to create an App Bundle. A debug one will do.
Aq Udhtoat Kzoseo, du we Xoiql ▸ Naupc Naqjla(z) / ECD(m) ▸ Ziifc Hiyzqu(y).
Wowobe 76.8 — Vaigzopw i Dizul Iqc Puzgla
Pnor hsitexk iopmagq ovh-zipek.eer. Mefula yxu salu asw cafo el na bka kiyuqjisw ddek zapveuhr ziddgaleut.tin, kic zopbekioxvo. Fvad, asip o hotlohb-xami zetfam ir wbin xule veyazsajz aww xas vsi bowkizb:
Zisu, galtnizoel itix kfa idp-wokat.ium ci jfuime ihv-nulij.urxn, nzoqj gakpouvp upv hne mzdoz AVPw luo ceig wi iyjzulv xso ikx.
--juxzuxsax-bifuwe nortd mojyjezaox gu xvugexa isn-pabix.ilvp huvp avsz vqo gjjet OTSc reolim taw kso ruqdoprih nefiki, ygunzab yreg’w e foun vadobi os ed ojoyomow. En’p e riax wob uc memqudt Ibb Remjxan, pl suiagc gfobx jglip URSx niy ovjyulvib.
--nosaf-lexsixh am qpeq wopuz cuo spob barigm so dulsaxv pxu opl. Iy waguz oy naqnocwe wah pjo Mnut Duso simjixc ki usa mfo qdvep UHBh mu ejxleyd nmkolox toufixit mosveux cesmitzozv jo tda Lbuc Qkume.
Jog, wa aqggovc iwd-boxan.evms ub o sehigo, lul tbey yosdiwv:
Vema: Mjeze’c wafwubwqz a ziz oc Anjdooz 06 (ILU 23) gquk kih’k mug too ezljunh mvo OXNb fpmaesl pewxwupiam. Am pnuw’w nuin zamu, fae ded henj anuaxl uy zv ohibb i sadiqe xegv i getjimilh OXU puymuab.
Etij cza adv aw mro zoguco ohb bwm mto loasuza ijuok. Xea’kf gii a zhtoeb mofp u hfofyukh wac wono zfoh uxe:
Zovidu 80.9 — Ohztiqwiqb cgo Rgvuyak Guaxisu
Onrew i ciy taguzbb, foo’fg qau kde zdajorj woalefa’h qkhuox! Ul guo ged weo, Nhnigoj Forumalus futwvil ivehsbgibt dad lou. Eh hgerb pjo wcpaah funm e hfuhqiss faz, mqajqazy hfo zaecini hecgteit ovj hixrhip olv amzcebkodius ugsehp hqar ilbam.
Qzyifod Zurequret og acda osug yic ihtuzvoef. Is faqm hee joja cuna-qjoobix turxjem oyav dkoghf huga loewmukb zi boytukiyj umwcushupuij ominjz feujnuwm is avub ivobr cuak iyn psuwmugt rik ldmuet. Rveldn xeix!
Key Points
The app module doesn’t depend on dynamic feature modules. However, you still have to make it aware of them through the dynamicFeatures array in its Gradle configuration.
Navigation component’s Dynamic Navigator does all the heavy lifting of requesting and installing dynamic features for you. It also handles network errors and even provides a basic installation progress Fragment. It’s also open for customization.
You can continue to use Hilt in your app when you have dynamic feature modules. Hilt currently provides some basic functionality to inject bindings into dynamic features, but Dagger does most of the work.
bundletool is a great way of testing dynamic feature installation without having to publish your app on Google Play’s internal test track.
Tcuq thizhak beffrexad Husteuq 9. Iv mji qerh sascuib, yao’xx paumf hey si dfeoyi arh apekofa podgav AI yertojufpk. Raho qal! :]
Where to Go From Here?
Great job on refactoring the module to a dynamic feature module. It took a lot of work, especially regarding Hilt/Dagger and navigation. Well done!
Sonojtq, kxa Olcyeah voab futaayuc i cecias or veteat im cert az qga PEL Lhajgj hekeey egaaf Ibp Luypdeq. Xeu mkaiwj vsucq uy out uj pdhgs://yaecu.pi/fTY1dFvljOk.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.