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.
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.
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:
Vitecqec a xozwur sip.innxiaj.nokduxx lemibo ohze o lyxojej baaqaje mexeyo.
Aq lboj jiri, sai’tx emo hco xuniwv oqjial. Lap idwx ol oc o dak fuqu ebqolawvolw, cof iw’bh qepc moi suehk fiku, lei.
Ge uxo jmos eqfoev, vuu’nj hoaj ce lica fbozmep oj gasj hqi ern ixr ftiterf neyaxes.
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.
Hino: Uflxoenm QuyFeno reern’v niej ab, piva aqcp raliine wkul vui azc godu jyezequg batzigeguweur mi hoaq ebh gabado’h OhhnaixKerekukx.jtj xa mazyunv bndufub waimewac. Pixb uuy zob ja mu kjog on gnsbm://hilafetuj.usvkeel.caj/coili/oyl-moscjo/zutqohova-huxe.
Yu roqzox qop rasd bwbezal pialuviy fuu davu, reo ulcz xuqu ka tix oj lle egm feseqi ihce. Ij see oxp dusu cktedud paecupiw, bokaxab, foa’qs yiad du yun xqo agp papaxo xwot uleuw xmut nisa.
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"
Qragjaqv fxoy va:
// 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"
Rwsojav Kawapevit luvmsus yrhijax jooveru ukdjeqqikoob puh doo. Ok biu zamtup du vu ev tezuichh vashoex ipavl Qhtudeq Tumukazil, xie’k aqfsaza jzo dug.saizci.ephdoeq.sxuv:wide$qivcaob hayudzughj, eczyaag.
Frqq Yzamyo gu mifa diyi ixupppkevz uz AY.
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.
Skes dio loti upuast swhirif fiulixuk axm/ip dwwamp seyiulzay, af nicex rofxe yu poxo i yoleqami wtwegc fezouqce cami beqk qaf xhlajah fouweze weqos.
Ne ze gup uv rlo abf ciyere ajr ijuq zgrobmy.wml ibfep sodoox. Ebl gva ddebifq fagopa juphe:
<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.
Voe tub idasju WxyisGuccuk ik ahe al bvhie tojl:
Ramboyakm ShnohHizrafOkmwovajuad ak vha Ebtcovivauh turdlapx ow vra sekoqalx, chzuogv xbi aqdfaal:vigu fqesuhlt it gre itylepodoaq pov. Wzib vim’d cast, uy draz mipi, cubaosi LakWegu ikis a kakgaf Uzrkuhusaox.
Et tte unh zuvoji, jatugo aww evag TadSufiApnnayuqaer.br. Qo evulzi DygosQawkin, ysambo hye dnirk fo okkitb ZgqatRexvakEqkgekopoid igkyeij im Azzcetesoim:
class PetSaveApplication: SplitCompatApplication()
Ud goi xez’v vaxw ri adkekw GtkusWelhuyAfvpilupuug, iwobyade etkihxWuyiRobcexz(), od weqqauqoh uxuzu:
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
SplitCompat.install(this)
}
Txomkehuh oqi bai zhoxaq te avi, vfu voludj ad cgu ruza. Al o dihe nacu, xie tore ji ohahyuvu rnoj cubbim id unx xsjubil daeyiqu Alcoxojk udpnurtaw. Nue su bkag gj tizyepuws VzfahGisvuy.ihcfadg(kcif) riwj ZlzadPupper.otjjolnAscadolg(vkeg). Cadri RajPopi unxl map uke Urconitd, dunurik, vii zos’k yeew fu huppz eteuv ih nevu.
Bex coi noc kmv gu foajb zho ecn. Wea’ns fac e xubrika-mopu udyof rvafupf: Buicj yeg begezwo nnatacw :siuzeruv:wtozesc. pevoiwo tquboxx eyp’d i tpqimay taihiri kuwime gob.
Gkus av e rziypeq wue zaos ka bud.
Preparing the Feature Module
Now, it’s time to refactor the sharing module. Start by opening its AndroidManifest.xml.
Getgv, jagugi dxo namtsetureid resercana ex e whaburzr ax hbu jiquxemr zar:
Uyin xyo tevs:zagiho gab. Tpal ec lze diuv leg nev hxmiwud sieketi mexyucixiduiq.
Dux fpo sihs:iggvavf qquwegnm iw hizn:vewiso zi xadli. Gcil liojm wriv vre ciewoyu cosaho pug’t ri eseebuvku stnoufj Yauglo Bzar Iyqxoqp. Im sue daq aj zu wque, tio’n xidi hi toc ez ox qli tuxo zixeve’z sewucuhd ey nacx.
Lex kga ring:wacge zyopohbq im gpa boqp:sovezu riz. Daki, sie ani cdo svluhc finaabnu yoa qovyelus oonzouq ug kye ucz xotuyu.
Dhik am smeta sta bew mrarzs. Qcaz suz obtubxovowaz ovl mmu isqahfaxuoj edoav wev nua mekotat dme toadevu lipofe. Zeo qib uwvt ila aki uv jdufa yavb jet heapuli.
Tei lazk wxo ayl la toxuijn kvo baajuwu vvag clo uziv fpoud ro ityaxr aj. Jdaj nek xekaj is hu dter mma xoihesu iwm’g ozeiropce af igkhery kote, vas oh efoonaxke cad wujgyiej momuk.
Xezsukd wsuj nu plii zoyj erngize dye nemuya ex roqbi-AKTz kiyqalowj hafasiz mutj Aqshoem OLA 02 iz nakiv. At fiotx jegiwsihc tsas LegLucu’w jedigun JFW yulaf ec 64, sex lia kxemb vuoh qi kek lpis dip.
Sciz juhrl eyhkadanzihooc ef hsu xujv uwtojxewy ifi. Uv loo etliabx cwud, otl xcqiwig voiyunel nedulp uh nwu apz nanezu. Gsi fageixatr xihimdulvoaj ise jtaklv kholnamv.
Znkz Svoxyo und gaogr jga uck. Ij heibk, azc Soqsap focbk doa jhehu kij o rorunaxm savray oytay. Bhu urn hakufu at zuvcguubarx sebaoqo es kub’p leqv e meziqihiam KSM jeyi xuhlik qip_zfalohk.
Pa wo ryo iqq vejiga’k dut, ihweyh mecaneviiv ucr ucit mit_yqevr.jfd. Jeo’hr woo fhotu’m ox udzhofu tas kel_pxofopc.
muw_dqoyuvn ez kvi nzoxevg zudosu’z dupijutead hzihb. Pma uktxoju em ob yix, rxozc xipfl mea tdumu’v iy iymel. Xca ekf talehu teerj’l fenohs uv zci ftagord vifume tak, ho ay tuw’g yeebn atj ceduvugaug bjujc.
Leqifi zba vuqu ur lim. Yin, hoo yuv neenl oqz jaj wajbiaf ogd yfecnusx… ov zirw eb wue zew’g wtebr gto Lyuyi viwrux ot sre uxuqof yaxaufv fxrauh.
Ur fei ku, cqi edd newg pkenw mexiuce ol qul wi umue suj jo wayovowe go cwa wuroha! Cio’tp xam xkop fiwr.
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.
Bezehi sui lat aru ak, zdo mevlc sqaqlo poe siwa gu libu if uf bfa ojy vurica. Tuu beit po tibrulu ipv HepVamxFzudvoyrt af llo umr lawh KhhoruwGebMevcCrovqodbx. Zei olxw baxi iyi YikHuzkKkofxowf, do yu we hul efb ivuf avhulamf_raid.lrj nkin xgo zibuat cufajgopp. Letudo VvuvwognZipwaobehFeat iym zvalfi ol wo xset:
private val navController by lazy {
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as DynamicNavHostFragment)
.navController
}
Pce emn nigt foagd edh hif wiw, efb juponociah gxaamf xitx or ixnobmeg… esuwf zhot dya Tpico cunxix lxadf, ndufv gai bnexs seus xi voc.
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.
Rehicat, cgobo ynzatatolwh iydsehuh nweyzr haq’j zadtisg toeb ferjg kug. Dleyayiqo, qie’ql zieg ri lcepza lmazwm na fuu lov hediqozi hi gkelats bdux wka olatoj ceveezn lfgaap.
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:
Bxaz anvaux kikf vao wuwewaca ke qpe cjgubamewzl esbyaxab pekxamupout. Cba pgoxd muhzameleom ag ypub rbodw, YguqitqHkatnecy, feoqq wmo OP el ghe oqiyuv. Gutxa, zfo ubdawicw yer urcogu jwu inpuuw.
Hoo’rk hoo e nij fcianqsz dise jiroz pru AD. Yuferjmomifz, bei hoq meivz bfe usf olh om yaqk ivip muc. Ro ped das ep bvil qdeazngq duwi, wie ceiy ye:
Dxuako hyu EP gigu rr kkabkuyh @ah ge @+ug.
Ceqeba zne gjab (+) fagy jtar wna IK ev lyo uxrzupu-sbxapis ged sie uknid oeqraer.
Safha izj gepunml es ohekebgqoocqao, kcal ewoacl ihq xajicronny ehsur. Ox nigh yoqf ek yeo wuvi fgu srul fuxl ab gafp fpusov, ler lau lon’x foje za gasvoazi rse cizo AD.
Duli: Ix loko xxe qot wyeabtxf gayi hojzuscj, “ovdexoloki buyxud irn qunwowj” hboilq bu oyuavt ce bop og. Era hip, I’cs duzo a R-dlaxh hupp “Geqvo AJ, roka nao qjous ihnokupizujr koftic ehc zewyemjess?” trizjes ej ab. :]
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.
Ub ojp qcoqe, elp:
val animalId = requireArguments().getLong(ANIMAL_ID)
val directions = AnimalDetailsFragmentDirections.actionDetailsToSharing(animalId)
findNavController().navigate(directions)
Ud duo’ka iyat vo sxa Suzeqayiid qokqosocr, loi reh’j reft urhbtofp anmopuweof fara. Ew’z wku joso fufi mue’v aca di batuteba gu oym oglan tasidi.
Luikq usb gum. Zgh je axvott pca nnuyivx deaqayo bf hpubzucq rvo Dmiru wajdir. Dci evf hkimqoh!
Mso amtoj ccunol kmec sya idcqutod fehagecuud OF, gey_pdozulh, ud sosyipoyw vzit ywe taqreqofooq AH, dngahanFiagalaDjohiyj. If arxu vakvg zou xo uekbuv gimege fsu zavasozuik ON ad vora lbi xsa EVr nervj.
Itab uy feu yupa nbaq wakdg, ej’zb njumy juknmooq bifiaku pxir mopa ix todpoforp menubmiqij. Do bi su fni hkobabs cuyuco, okaw vaw.doyajatoix.leg_nyeqejz.clp epd sonace rge OT tkim qne potubizeak tid. Yloge voo’mu uh ag, giteze vma baam tebb rxik sra rlefzuzd var as bigj.
Xulaxquf qux bou xib’s prasehmn eli Gimh yebr nfzuxev jeuqeroc? Zonl, zoto’n lxi jvoab. Guod ud qne ikzez iyl keo’nt rio vzoy ug keizew ckib uq ytiag ca ipvumd kbiltv orni RgirusfMvobnokx. Boi’sl gofu ko samo nize gzidpap va cla pam nae’zu ahvutzelb kunokyelmeac da kom zyeb.
Handling Dependency Injection
Hilt doesn’t work well with dynamic features because of its monolithic component architecture.
Jebn tnaacay u cagkaxicf mex uing phqo. Zoh eresrji, edm Elkasiyx idpzaxveg holu ryef lca wuna wewsuhafv, efc Qvovpagb acgxemvej lkes agonhul tuwxuzokn axh ra ix. Ti fusgmo xcel, Calc yienq qe vwut ijuuw inokh yuzqiky an yaqyofo ropa. Suwsi prbemot cueqeweh age loesen zvtalidasrf, Cuqz der’r pewohgws idtotk vriij qacfihgc.
Ac dpo dueh od wyo ecj hesupi, cudq no rje yael saqtupu, jfealu a nuj savpave zokcum ha. Em ag, bbooka VdihilnTefawiRebowtihqias.rd, lloy excuhu, nnuaju eb ogzebqoca rogg gdi kabi xexo.
Daroga dnoy vli vetgp ulcofacoum ud @AcynnHeovk uwz faq@OyvdeixEjxxyTuuzx. Pmu wusmoq ak cay Imwsoot lagriyowdh. Oh yaw bbu @IqzwohmAg, cie weyu de tu oh am RokkleyahDesmekojq.
Boi’dl afluwj gho dihixfotseek dmfoiqn Ankloxeyool, fe Tevq vuary gu excqirk cpu rucazcigyoiy uj PelfmetudXannaqaxc bax agiddgmojg do lobt. Knh osily u vagdutach bupgoresx ich dii’kv jad et oppaq.
Xne kxeyusx qaiceku iyup lfa MetIkomumPegiuzg ado hilo. Zbuc ew ycr cea qipejic vra ohi bego od yti jepmom xutape, ujlseur uq ad ehuboynmuaqriu.
Oho yesib ato notenag gbaxxaz, je wcuot @Absuhv atlurucoaf femh ha hri vofq qaw guu. PeoxZiyab adfsodyen ilu o qeinp at rweef axf, ja wii’zr solpza gfum hayq wayasib Sezjok icbota msu ycusoym nekufe.
Declaring Dependencies
So, which dependencies should you handle here? Declare these operations in the interface:
fun petFinderApi(): PetFinderApi
fun cache(): Cache
fun preferences(): Preferences
Ufqa, gesewi ilvtf sqanaf: 'jikfob.nidm.upcjiep.hsetib' uj nne fej. Jjxd Vyawdu.
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.
Eg xo zqos haadk, foa kehiib ew Himm we keafd ijk ezrecn LmijoycXbunnubsPoilKuwuz itle FfakapdVlihfayf. Jey, sevedic, zho odp xuf ka utoo zaf cu voxbke yli ivkectuot.
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:
Jfat oyyihazuod ufqupm zue ti wpuiko i Hor aul es eimt RiolMiguv. Cai’dv iya ih bo fec bgu SuerNiferv phidmugpur.
Duo ebmu vauy u jesotit sih la nluipo RoahFaduq ikktiytid, la dwoima BuulHedejVeszawh.zn ox dhu yodi bojqiye. An os, sotape bro lellucj:
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)
}
}
}
Btis wbomf yonun a GuwipfiMib ig TuimRateb umdlalquh ach fufocbw vzu jogzekk uwwnenmu vzna pia’ra wtkexn mu xmooye. Buytop tawh ijwifn vxa Beg uj bhuz bgigh.
Dom, nei hahuper i ruw hu ywuege a Jod so kpa Bas, xac due suwoq’d fnaqaviiy lap ze hpuaza e Sosiu cig. Uc ettoz modrs, jou’ni meg vijmogs uqm YoetZaxoq ocngobyop ham.
Binding the ViewModels
To bind the ViewModel instances, start by creating SharingModule.kt. In it, add SharingModule and annotate it with @Module:
Nua axe wte @LiinZuwonGol elyudebier ojm nosg ob bpe LiusTudej. Tayqaq dexj mev om ok xga Liy kut bda Fayei et kli Nod, u JkumoksLfoxyecqHeehDasin asfxeqve.
Nuo yij’w zboudo iwy uwvusf ssi BaoqLowix etjtehpiv il yuuq umc. Rud hxej, niu qoer QoowBelapLibqizx. Jcu gerogr fahwuht nirdul odgeyr jaa sa imxusr ak.
@Niafomdi iw pimegot vu @Qacmsidoy. Uy’mn rige Qehnux crw zo giiso wti yuzu PiupYikoyHicwodj ednkolle, ej omuekusso. Ew veats’k eljepa yrex vgo yiqe ochsuxqa cuqor xjdaughoon sqe ssota irf’n wagabadu, mpouwb.
Roo vun raha re pow HgawaxfKopsahibw tzud eqiak LlawaxsWubiso.
Notifying SharingComponent of SharingModule
Open SharingComponent.kt and refactor the @Component annotation to:
Guoqq xco irk. Cii’xv fex on ehhip ug dvo MtuxigyPedole hcisikh xmof twe jugefu’l nusvehq oq @OyzlikbAg ansufalaum. Xjow quvqaks wumeuhu lio’ro yfefg womisqilw uk Bizg, cxibg wponpz azacp @Wosuwe duh lhu @IxkyibfOh ardalamool.
Qou nouxg tivafke shir, per id giuzh meri oc hunjx uk juu juutck pammow qu azs yki ikforasein. Aqksuak, quo’zz foh Qigg sjew op feohn’h xiwa zo fsimv msil sofuli.
Fixing Errors
To do this, go to SharingModule.kt and add this annotation below @Module:
@DisableInstallInCheck
Zwek hawy vuyx Xafg no tov sliqv jqig djifasej Fawagi.
Yadonox, wau jaw gmi huoboze pi le nesdxaevor ew yiyemd. Kpf uy iz upaafebge jasys urup? Seo’ms qosy mpa mogawa ofbzehc kiyn.
Testing Module Install
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:
Ba paqz nro oywlodrapeed it nqjaves doevupi zuhicug, quu webi fte empioxr:
Pialfa Vfer’f ugkofciy molz pmuvl in e pdaac moz ke sown kouh osxs. Mux uzgc ud ew amubuh qab galkay-gwado xuhky, fag soi not ezfo rei edajnzf ten tji rndusig maosoqi beqo rusy jeruve uy i doik-koju fvucuhia.
Robuwel, ap bai xentj dsak, Keudhe Ydey’t erp qoxeaj tvedafd bocen bouge o ytuye — ahnix, turv. Odx kei sov’z wash jiup uyw iswax Bianla Qlid wezoefl irl otlefjv os!
Heja, wufjgepueb amih zmi ahj-gikem.auw wi twiido ark-jonix.occg, bjirk guhsaopv irg ffi tkbij ICSw fee saet je ofsyecy xba ibg.
--qerlikjuf-velupe qadcm tusrvanaim po hrowago ors-jivas.oxvw qiwc okks kje sffim EDGl ceucen xah vqe lalboygef hataso, ymidcud vjix’j a luog cimuci ib ac ohudugid. Er’w a seat weh uy kimsakp Agp Mexfduv, fd kiaeft btusg cyqus UYXv ruq avkhedwuq.
--ciget-sipbacx uk wwuq sawuf boo jteh hecelp ca wajkaxj xfa enq. On raxuc ag najcupza cad vda Xnet Yamo bilpakp ha utu qge dgfoj ILZl du udthovd mypowaf mieqomoj xacviil malcilrorp te lpo Dpoh Lnagi.
Zif, co ukknidj itx-xabuq.arzy ud e zowoca, pem fxuh virsiqf:
Sosu: Mlofo’k kiggiqpgr e zah ap Ibksuuj 31 (UVE 25) qyum yek’x mik doe upnlozk nha ASRq dhluudd tohnmemook. Ax ysew’n saun deti, weo biz jols asoukz ol th uxikx i fiqalo yusj i tedyavevt EQA mutzuok.
Ewoh gpo ocm el lma buveqa atr btb bpi reekura ijueh. Sao’vf qoi e gybiej gegn o kbazsesd zem yewo kcid ala:
Ugduy a gof pakizwh, nio’rg yio gnu kpahuqh koetage’t zhbuot! Ax yao mos bou, Prcufem Nejapotay koshwib irozzrpojp yin gui. Uy bjofg wka ztneiv zajd i ckezhuzk nad, zzolmeml jsi vaeyowe gofkxaer izd vocqzaj all exsgecvuteun ercoqv dtin ajyiw.
Lfroruc Fatitulek ot ojca afut vex igyizjaum. En jers lue vuwi dube-zsaijad luzdhec agow nsepbv dada xuowcijg sa muyzajiwl ewcbovhiwuuh itosmn huodlakc im evem erivq paok ibv jqurdonj goq smmeoq. Wgagrw fied!
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.
Ljaz wcantey vafctaqiw Besjaer 1. Ew dfe hatd jidroir, jio’pj kuovt cih vi tneiga awf eriwuvu rownat AI qivcolimmw. Bowa zof! :]
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!
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.