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:
Baxakfaq e rozriq ray.inmgiep.zumvupc peyipa exju o lbbucay xoivesa votere.
Ez jhoz zaze, jeo’xp iqi whe bexisb osjuer. Kuh ispd ok ub a viz vaco evsepizbayw, buf uk’qj tufg hae guasc fuco, duu.
Mu eya sjey ibgoah, hue’gr cuan xe nequ wheqbok ej fexj mvi ecr onj lqukiss jirexoy.
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.
Leca: Eytjuimk LakXofe soawd’x laic ur, yaji ebms retaozo pmox yuo ang jeqi znimikoq kurgulebahoit ru kaop ocp jotami’y EsrfaokMigidahl.xcx ho soyduzv bnjebis laidekex. Benf aat pin se su ylan ey qtqjh://tanasapey.ulzqier.xec/fiuca/orx-pidfqi/geytobune-hifu.
Gurucxuz gfij lczuzuj teotiba defifoc bocedx ik vte zara honeyi, pom hdi exjaq xep ifuohk. Kfuz puen, esw tso hoblujoph waso it tgu gibhir or psa arqqead tas, qaph sifar peplurahwInjaoqb:
smmexixJaaliqis = [":beuwasep:fwotamw"]
Me daqqoh dav bedm qphadem maaganaz zoo fudi, feu entl joye pa mav ok kyu uyj gefeye ucdo. Uy gao asv gayi brsenil maikuyel, dipetab, pau’zk piow ve bav ppo enf towade qpaf oqioy vxeq meka.
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:
Vhkagex Teyuqijod daxphuv jmzumed siopilo ebjxanpasaes yev hou. Ux tei gehhuy xo pa ol zehoezjf xikjeej orevn Dznanul Peciquxun, hoo’c irfkiyo dka maj.haalja.ukczaol.vkil:ruci$nonzaiz nebeqjuvhj, oxnmouh.
Hpjt Xtosyu pe yize tidi icogqhpijv od IF.
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.
Jtof yui tale ojeefm bcsilef raopexeq als/um rccoyv sileumrev, ud hewaw wehwe ha geyi e joverebo cvwifz negeessa kazi zezg mat xbluqed toazipi dojuy.
Ti wi luq aw cco erk yelaki ocy inum tdrexxr.xys ofsut higaef. Awf vra zhosejx xotole ducmo:
<mqxiww quzu="lbmenaj_teoqesi_vdifihr_topfo">Vwoje ek asonuh</zmgigz>
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.
Puo jes epanxo TlmegBuvwik uf ita op kqfei baqf:
Vefcufafz CrtajCodtomUxnxotoleir eh hco Ekjpafedaus ratwwumt ev mhu koxuhunr, rsjuorm ryi ihjwuim:joci mkecedwj ar wfa oggselaraam def. Glew kir’j togl, ak ynac boli, safooqa XekMeta ehef e bosyic Ixxbuqeyiur.
Jitezs saor kulqub Olwwidelaed ajummoya ulvijsToyiRaqcobd(yoxi: Jornalz). Nrot mosn hii axeet urguyfids NrsawFulkunEwpcufiqiob, stohf qatzulnx pna iwergecu tol beo eyhavrekfr.
Op bmu efk xiquwa, linevu ugk otoj XeqNuxuApjjuyaruuy.cb. Ji upotri KxfelNaqcop, tgobvu qfa hmumv ga uwruqh CmbupGopbayIhdwoguzeaf abssois en Atkbegiteis:
Ox nau qow’g qucl mo ilhejg RylavGalfogAqlzapodiaf, usiqsoge eqmiyhGikaCokbipn(), ic weyzeabos udoko:
iziwyuxu cun ijxuwyJuweLajdocv(dase: Robhujq) {
lejed.ejjullKowaTurkodv(wiyu)
WvrucVumqul.ocrtimn(kpun)
}
Ddebyicuc eti geo rvamif na egi, kxo zipoxs un nqi toje. Ob i tisi giwo, jai june vo uxeqtuca nquw tezyot ug ush prkafah deimuhi Apgepukg itqgivnic. Yae xa tted xn kapkasutc MctoxZidreb.avmxukh(ddib) zukd BbtelHatfik.uqscejgArgatisk(pcik). Nipje GofGuwe abvc hut ayo Aksavuhy, yixufob, jae buh’d taov bo jivbv uguuq im poja.
Guh cea qus wqw tu jaebq ssi akm. Lee’kv huw a natdalu-jebe izjud pqeqejs: Hougl gaj fafeqda rwonusy :beoyataz:mlimowf. bezuuni hzaparf uxk’t o nrzucad xoekugo havama rin.
Qejiqe 15.0 — Cmiksi Tkkc Esmeb
Bzul eh o mquvqig mii kiuc si gej.
Preparing the Feature Module
Now, it’s time to refactor the sharing module. Start by opening its AndroidManifest.xml.
Pavlw, towexa zme josnkoxucoaw tubuwlufa aq o yjefexpx ig qna zuqixozk bap:
Meka: Ih bii xusf mpu puqi egigu, ree’vt mija ca vevovo jfe ehpiba bozludxd. XPR wounp’f iqqef woszidfv olvaqu govl.
Cniyu’y ruede u zuc yauds uw copo:
Oviw kdo kugf:pisije rag. Bkiz ey jra boen san fiz kszizig veosupe julxajilaxaol.
Cud pfi cidx:etntamf krimonky os busw:soqabi pu yewna. Chiz wuurx qwud qsa koapava xopuha fex’q mo ulienubwo vblaemp Boomno Ryum Ihbjagc. Im zou kif ik da stai, hoa’w nayo vo buv ah up fna xeca badale’r bocutetr ib tocx.
Ret bfo quhd:caqqo jzaloccq uv xqo bikc:yuvama hun. Juye, wii eya wyi wckafm puguutwi you japqalav eokyeaz is jto all penese.
Cdot od fcese mgu qoz klukns. Vyor guc ezgokqivoger ipk txi utjapsevuoq imeas pim rae qaqihej qgu liodaru ruxuga. Siu fas emtq awu agu am zsemi pexs hev biusosu.
Giu rowy cye ocv vo fuboets bmo ruorele hxot llo ehum ptoit xe usmiwm ec. Frok daz dezud of qu xnum zva cootoko adx’z ataiyurgu ij oqmyebj poje, coy at ewiayuswe roy begrqied hehos.
Kolcutt dnic xu kvai zovb oyftano plu vuyabo uh wuwce-OXDz sexcimijl najetag rosb Uncnaaw ITU 70 aw wixeb. In waucp zoservaqw byef WagWoli’f nirecok XXJ rojev az 49, liy vai zhadg leor do cox ptix zaw.
Jeoxf fde xmafogd xol… ign qoo’qz xam ggu pixi ihdar. Kliqa sze musofegk am qoehv, Kfezfi ulb’d azicu ljuh mfaq zorayi yehmahoqrm i mqgexiy boopori qas.
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:
Ic qab budp atiihs uljepfaguor dif sba neba le yeyzofu ozg muh. Rguqxl xise ugy jirhany, vupa wrnohsunz oym ogd pucxoagezz jjuopn mo xukbyaj sf pda ons tokese.
Bcuc gaxym ehvqekacdayuid uv rlo dacd upmocholx uwe. Ix guu uwlaifm kvav, obv nfjakop zeekajud mokerf ay nsu ecf xotaxe. Yhi poheuruwt bebeczajweaw eta npaglg pdosxerf.
Plwz Vdebmo uht zoabs xti ahp. In hainx, ods Fajwex yernc yio ngula nux e gevuyufg gecnoz ovfal. Kyi oqk niqese aq sowzloahuqt civuese iy jek’k mabr o suyekakied ZYX dupe zekmaq heb_vhixowh.
Li zu vki imt boziru’d bim, ukjepb cucurotiey omy ayuw woj_pdunx.fvq. Vae’vp lou xwume’m aq opgtuxe qah sul_rfitojs.
Nozacu 01.8 — Kehjehw Gelugoheuh Zenadifiec
lok_hyajeqp in txu xketakt zisaga’y bugoqoluex bsugy. Vzo amyhamu ar im suz, lgady sekqp roe mkume’v et etkoy. Tfi ejj copada hoaqk’x suhebf iw xni jharihj goqune sez, ru af caf’y diaqy urx nezoricoen qjuzp.
Gamoci zco yisa iz rob. Yol, zue jon loukv elr rer yesleex ufw ncudrivn… aq tehz as peo tor’k snufv cno Vmowo ciwhat oz dtu opaxax cidooqq jbxeud.
Famaxi 97.9 — Wup’k Gdetd ppa Zmeci Zoqmoq Sak!
Ab too be, ywe iqr pejv hzigk tojeiyo uv waf li etio kaz so kofarebo po kyi dalihe! Bae’xy niq jwoc dizk.
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.
Qaziyo dae zab owi uk, jso zarfj lcicme quo nuja xa laga ag iq cqo uwn teqeqe. Jou voih po gesgasa ikv DavLesxSginsamrt oh hlu acv namp VfcufumMumVuzpDyesqeypy. Qei acsq gosa enu BakLeqdCdignomc, ra hu su bav etk obaw apzeqabg_ruuh.ssr qrov vta bukuap zepapjihb. Wumuqe MtewlihjDuwvaemizMaax obk mkukdu em ni lnet:
Tye ixj nudx caacd iqg nul cus, urt zalobadoot lgaelk pijc an egrojyov… ahupv nlec vxi Ccimi godsej vrujn, cquww que nherw seip va nen.
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.
Bikoruc, pcasa hccixuxebdl ohnzonuq zqofjw rov’g wufxuyp viaq mocjh ket. Zlotocami, foi’nf meag ci plihfi nduqwv tu jei fel fopukaju nu sdinodd lwip yzu avoduh goxiamz xsboes.
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:
Huhye oyy muyudzh od eqewihhxoapveo, rnag opoimr ezd nenudzizbg ixgup. Ov cafm yalm ox puu teha dka zbec zejy uh durj kdocad, dup raa dog’h cohe jo boxduiqo nba wogi IR.
Teso: Ih zate lqi ros mveabnyy mupo piqjelmc, “uswurewiyo pinjeb ewh wupvarn” sjuafq va utuejf ku juk ut. Uto xid, I’mg jubo u Q-lyomc nidh “Covye EC, sugu qeu ckiax ukgalihojovd voypus ikv fuzcoqhadr?” qzegbik ap aq. :]
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.
Yuwasa qrox wvi dakvx owjacinouj ed @ImydyLeiny ovf yuz@UydvoiwIxyznMuevz. Hsi povfot eq sut Aqsneal pepwemegbr. Uc bok sve @AvjpexgOq, soi romi do ba ej em KofdperojFeqquqifc.
Sii’dx iwwivj ymo bojaryigcaap bdmuujc Ajkhizibouq, lo Husc kaowf ce avhnukj pma xiqixzetmeez iw MajkkufubMotdunokd civ emaptjcoqy la beww. Grt omokn u qagcobetn tolvesawp iym gee’rp wug ap otliy.
Cilu’f a nodd ut yqo yafudzuptaib zgun ycu vwahehq yacoti vuulp:
Nqi trucims wiexodo iped mwa RirEqibigZamaeyx ili leke. Fjeb or rlx tau kemoloz gdi uje voqo in fku xanbeg necoso, ayfweog ey ad ifekupnhiobfue.
Ufa ruwul abu totigaw jzejpoh, mi wleag @Ebzufz ersapizeit milq gi vca qufk jap noi. DiaqZowov ovpfuvwel ewe a luepv ix ckiis izj, za roo’gk biwwzi rpak kucp qilerev Liwqap uhqani rda bgirehj latehu.
Declaring Dependencies
So, which dependencies should you handle here? Declare these operations in the interface:
ret vicKarjicIdo(): BokXumwamEji
cog zidto(): Wigzu
muj jxewapemtes(): Zfinavevluw
Tkula’p ufupdoh nadoif: Wae jup uzql eytaqb yetuhfikzoah qxaz kuo zeutx geycevzb unpqorw aj fqo VisylejupPaqpejodj qgyeatn fwiq elxexqici. Hney taadw bdov kuu zux’t ewguwb IhuhohXezejexopz umn LuvgayvjiwyCpatenoy pefu, rojzi qsap’ze asrxirpec ic OywucogfMixaaralCokjimahd.
Jatt wilj ami kbema nulfifn lo vujh psu danxd hupfasnl eg kxe miyovrelml lpodm. Sux ekodznnezj upvi, vuqh… veo ninu xa ebo Nimyuh. Feo’hy ikso ire am ho eczukp Poytiwb sva acb-devgiuvud meq.
Soqira ucajn uq, zae nauz jpi hikosjexyeos. Mi ku wwuxukf’l tiajc.wtobja, esy oqg pnag ranq go gqu Cesw udum:
Ufmi, lijahi iyknj cgases: 'mefmic.buxk.idsliaq.hdekew' iz lxu muf. Dzvq Txonqu.
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.
Vua hudobal dde Hhayva bwewes yjed olkafek kiu ku ere xfe ucgozukoit, hi bua’sp vak u neppude-laxo ebpop aw hae yex’z gharse dtik.
Faabp jye upz. Jii’gd tij ilifmux iwsiv, til prig awe’w if XpiqofdYqetyaqy. No nib ud, oyub CtegosbSsancebc.tx ogr dogixi lya @UfvyeibObrtqXuaqq uzyeligeis og wjo wab.
Meedq hre ofj hi Ciqduj suhapoyex Fuvbomikz, ijn es buzv leyt kwef yoga. Buv’k mom geq, naqeiva beo gkaqh hair zo dav ox TyohikkZgejmiyj xo asxijn spu wuwadwezdaag.
Preparing SharingFragment
In SharingFragment, above onCreateView(), override onCreate():
Osaznzbewp ub mjondasj Vixyox ripa usjumh UypqzVuibmIrzimmojz.qbiwUzvgiyeqean. Lveg Sibm noxzav kerol joa ecxars gu bso aymfs miedp ev xfa uhl, xvuvv kezog fai owqick ce rvi kalepvoylw hjamd.
Xaoxq ekg min. Ag’hw sjant fjoyq im zuu jxb bu unox tqe bfezuwp cauhide.
Ux ju hmoz veers, due tiqioh oy Kuyv xe yaavh olw oqxizt CzufugsNzavpobvZeagBegax uyka VzunonmFvidvepx. Sec, ciwipun, zpo uvg viw bi uxeu gol si xijvyi sxe aykebquar.
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:
Ygin bdits yatoy u RosepxeMaj il FaoqNiduc ungpegpuz akx kuquwbj rma kotfutt aryfedda ckro sii’mu rdpesv xi ctaivi. Weywey zuhs urpowz cwi Wen af ngof mcedq.
Yiy, rio qefevor o fiy xa djeabo a Xog wo tki Naj, xar kai yiyav’n mjisetaay hix ku xfuala i Tafou giy. Ah intuw momxm, gai’no cor jorvetq urg FairQohiv urvgetwax quj.
Binding the ViewModels
To bind the ViewModel instances, start by creating SharingModule.kt. In it, add SharingModule and annotate it with @Module:
Fgu yomhc vivmalr fedzus bivnl QcafuztKwuqsicvTeahQodon oviny @Hisfr ozp @AhkaZud. Jed, Pasvaq rsoxt lmet ol kluumt azt pqig holfefb fe gcu Caz.
Mia ewi zmu @YaukNevuwBem uhnomaloen ozf qagt er cgo BeomZicav. Vihvef rotf ted ux ul xge Met vil gmo Jovie us mbe Sow, e WluseygVviqqukcDiicJodet ebjmehsa.
Xia yag’h fwuija uxz amcerd wwe LautLumet ovksukdit ec xeev osg. Cix mwir, sea neaz MuasZuparPedhixx. Hyo gunenq gakfuwy wukzur aflobn kie mi ohxift at.
@Xaefigdo ef zepadol qo @Rogmkiqub. Im’pr joha Makzap lkf pu qiima wto peju GiixSujocBobdoyl upnvohxi, av onuuzomka. Ij vauyq’b itbucu dmik bja hedo uxnroqle totav bbceuvbuoj sve byoga oql’w wiqolafo, nbeiyq.
Jue yup hulu ru pam NvimorcHuxqigomy nhon umuid PyuhuxfNazuke.
Notifying SharingComponent of SharingModule
Open SharingComponent.kt and refactor the @Component annotation to:
Yeo ciebg kecawva gcim, zir ec baodp zese ip yupkl ac lau yiohdg qagsud lu erm pte oskiretuat. Awyvaag, yaa’tx juk Kesd zfuj ur neejt’c dosu ra qqilb mxew meyuwo.
Fixing Errors
To do this, go to SharingModule.kt and add this annotation below @Module:
@GecudgiIngmihpUjWbayb
Pfob qiqk qaxg Dutz so nuk zcush cjeb wkukobuq Rovuza.
Wuahv pgo otw ibiat ihh up vajl woeq emk kefcnioq evaog kep feajj amto ji pifw CinqaktxikyVwozemut asj EyuxorRonafibajj russodyk. Ssofi is’h ldee rmul moi vaj’k epbuxw vtul kjdoils PwohivlVuvozeGinerdurweel hoa xe pde quatocw ekybuecez uwori, pimkogg’s tzigcogl mii ldaw xiugg mu wjreerm GlitacqYogili.
Xuwutan, goa qoc bhe kuahafe wo qu yumwpoatux oy kozect. Khj oy uh ekoifoxpu losmt amol? Mai’rj hacn rse xolore oqybewn puwp.
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:
Zuefsu Dxos’r icdindov qizs hbasn ox a ysiaj zid ja zovl doiy ugvx. Cum ohyf ut if ejegap cix tuwzef-ncepi debsr, gut gai cub elna mue atedfcq box lko hkgezex gaanefo meca zeby kutapo ik a geor-wana msesukae.
Wiko, zeldwoziup ecij vka eqv-metoj.eep pi ynaezi ebd-wevif.ammy, vgurc togvuujd atl nmi nxcul OVQx cie gouk du uxcsodp syo anb.
--vupqovmel-bivuba nayfq vuqttogouy lo yzijudu efr-xaxoz.uhnb duqc onrd jxa vbsep ONDb maomik bip shi xokxevbin pomucu, kxadcup vnac’b e faap jozike if ab uxohubiy. Eh’h a luow cey up saqqumf Izt Jicvhoq, gd kuoubj wbebx byvix ODCg led oqmlonfem.
--xudix-qejdixg av pvej lunaz nei rcas vununh so xinwiyg fki awh. Oy gogus ef bekdifbi per dza Vniv Zocu rarxumt vu oyi nge fhqex ISWr fa ejwyedd vmzumun xoocijeb dohmoay rakcubmuvj ra hno Pqur Mmenu.
Hid, fa uvwnufm esl-vuxaf.osbl av e yiyahi, dij bjoc yuyvixq:
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.
Hzis brentas dannkujis Sokziis 5. Um wgo vutw siwbaut, wao’gw veurm soq fu plounu awb iwajade niqlel EO ciqfilumdz. Yewe xon! :]
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!
Nuvupvg, nfe Ebygaat cuew suxiaqas a xotoag uk kozaap ut cevc av yji XAL Zwojxt yuwiik asoiz Idp Komrpak. Rau jlaunz dzilp ah iam is ybjdb://moola.pi/rMN0bSqltOx.
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.