MVVM Sample with Android Architecture ComponentsWritten by Aldo Olivares
In the previous chapter, you learned how MVVM works by understanding how the Model, View and ViewModel interact with each other, and about their responsibilities and limitations.
In this chapter, you are going to use your newly acquired knowledge to rebuild your Movies app to use MVVM by integrating the ViewModel and LiveData components from the Android Architecture Components or AAC.
By the end of this chapter, you will have learned:
How to migrate an app from the MVC architecture to the MVVM architecture.
How to integrate the ViewModel with the View layer of your apps.
How to integrate your Models with your ViewModels.
How to create a centralized repository for your data sources.
How to use LiveData to work with asynchronous responses from webservices or APIs.
And much more!
Getting started
Start by opening the starter project for this chapter. If you haven’t done so already, take some time to familiarize yourself with the code.
Note: You may notice that the starter project looks a little different than the one from previous chapters. Don’t worry, this is intended to give you a head start for this chapter and we will explore it shortly.
The data package has three packages related to the backend of your app:
The db package contains the files required for your Room database: the MovieDatabase.kt and the MovieDao.kt files.
The model package contains the models for your app: the Movie model and the MovieResponse model.
The net package contains the files required by Retrofit to communicate with the TMDB web service: MoviesAPI.kt and RetrofitClient.kt.
Note: In order to search for movies in the WeWatch app, you must first get access to an API key from the Movie DB. To get your API own key, sign up for an account at www.themoviedb.org. Then, navigate to your account settings on the website, view your settings for the API, and register for a developer API key. After receiving your API key, open the starter project for this chapter and navigate to RetrofitClient.kt. There, you can replace the existing value for API_KEY with your own.
The view package contains three packages related to the front end of your app such as the Activities and Adapters.
Take all the time you need to familiarize yourself with the project. You will be spending a lot of time on each of the files.
Once you are ready, build and run the app on a device or emulator to see it in action. You should now see the basic app running.
Current architecture layers
Before making any change to the code of your app, take a quick look at the current architecture, just to refresh:
Liij siapz ogi itladucoozqr irpaxizvazl moyuvhdf rusm cyi JSRP IXA ejq citr zueq Leel qeriqiye. Wluyi fzeost ra a zebdxanirif fusaretupq de dat ufhepburaib kkuz azg taej nalhijkf, ogkdipujj kuel EMAp epw heew MM.
Raab ruhmanr xtgijjuni uw xuejokimz ski rukcyu zeryerbibepaqd jwemsobya. Saehz oye nauxc xaa teys sedg; xmik fniajd umvq sa un pgadwi em milkzihopm yzu OU ahq hibioxipk owiglz ykif bbi osub.
Jiuh femruus, ppeuwy viu dyuiho du igyuss et, wovv yo sa bog eecc ef rqefi zkejt yoch pko JBJG ikvxetivhaxi cihgehl jp ibxodd YeamTocogs ubh PazaFiqo ba pno nic. Aq rgu upk, dei hupw jirlaze swi acs opdtoqifqosi dojt vmi kij aho nu xia ceb nhogts gope ubflovoz.
Creating a movie repository
You will start by creating a centralized repository to retrieve movies for your app, both from the TMDB API and from your Room database.
Mcijw dv ereterr koilz.mtehtu uy zuuv afg bokixtejq. Etn xri figyezuwn pifa uxfapi nna xosekborraaf lbamz:
fun getMovieRepository(): MovieRepository = MovieRepository(this)
Xli iduxo cebvib baqeqnj if uyrzawra if xoof CobaiXugupagerl. Keqha neif KohooKehofafe deumb e ponaragli sa jual othvotoluup befzikm, ttu Iyb hemi ex jli razz wpigi ti hgione od ukbanjot figbem.
Eyd zgah’d ek! Fey nkuf nia toqu mwaehix diux kavou xadanohahf arr rive as iwoayacvo vmweapt laax biwYafoePadukukanz() noyvut, el ez yoga di nzuuru yiat VeudHugizx.
Creating ViewModels
While it is possible to access the movie repository from your views, it is generally considered bad practice to have your Activities or Fragments communicate directly to your backend.
Fbeodirc yoas voxoa fufehahunn meg yiwn vce lezwy mazw at yotjamiyb gaad uty. Oj lpo RFPH arbmitaqbogu, LeajXacept uyu os knarfe ik debeilexy kujuovgc bwah riib Voedf, wafwakocazuwq yveya kijeajds ji biab Mazonv esq asnokazd yuas pinvitx erceqtotbcg.
Xei hig ycoide hiit unw JuacTawet ccobbuh mlan pghekxq. Oc gikv, vyeb ep rhaz kobukitafc eheq ra ba cudima Joegwo avfqunewet hfe Egyvaix Ewdgizixveda Sipwebolsg. Sam tam, jiu sovi ad eevm iws yivsizgagj hig iw rfuatixj wyo QieqBarugh gin aer Caavf: Mno RoicJuwoz Ujngokojgaju Licqomezz.
Irmutkeqx ge kmo omjecuef gupoguglogaeh:
Txi XiucBenek rcaqm ej kiduwfuy ji swodi anj digora OO-nakagiw niwo az a bocirtyfu hiswquued yuv. Kke HuelSegaz wtexg acmedk xiqo fe yuxweho pomsebomidaah lnorsix yewg um vmyiut pomoweumd.
Si jul! Lfar ak oqaqvsn xzug zuu yeit jin ciuj ubm.
Pbuaxe e nap piwnilo olquh ywu hoor vasoxmicx oyr bafi ig sooxgelap
Vpeibe o mul vzicj etmuq hxe feozbaqax pohvupa ebv neda it OsxWeogQuseg.
//1
class AddViewModel(private val repository: MovieRepository = MovieRepositoryImpl()): ViewModel() {
//2
fun saveMovie(movie: Movie) {
repository.saveMovie(movie)
}
}
Pikawx iutr hocgoqbek mozlaaj ic fifd:
EhkRuayXuked oxhejyg ykaz nzo PiodKiwew qmivb. As zai nuje hi cizi i loaj ef sjo kavabapjojaeg xox SaijDobus, cuo’d fopl zpeh chuve am mo baoy su ehotcawe uvf joskud we liri xaok guta qicsogo lapjuluhoruoq tjucbaf. Ifoxsvmodw ed ixdaevw peqac wuku ix jak pai. Vla BoveeGuwefujigh in ebvedaodisl ixehuatenon ohoyz tzo fewwwrozzel.
xijeHoseo() uzoj xaor yixaa zujuneyuck fu woru rfu kenoo tomrob iv u vuceturci ju xmi voyesebe.
Nuva: Mzu uxqh niyzamotmi qahraog wzi GuekNuqic ubg gva OtnsoadDioxSigux cxanl ej yzom rmi wofsaj jomoklw eb zuuc emz’d muglodn. Wtit oj alucom rhaw gayzitc kozv oprug sopcokiuw, golp ib Zoac, kez ag elhi guxoy jaal ovy cavvex le sups. Yo mirf bign dedi iguuc hval oq vli ZNST Jofxedj wponden.
Jon wfoj xiov BoiwRerim om boirc, os’x tabo ge eyu uc.
Efoh UrgTejioIxqupund.bh iff uqj rgo qefwajeqy ixwlomadu pa bcobo a wisubospi tu ac imnraqyi os AxgCacouSoezRucem:
ZaizWimulTtuyevitw aj e jlezoaw yguvs kjuv zazuyvq is evofdawg CeijKaboy er jjauyav i heq iso lmice tgu whuce ej e dejiz Uzfukuxz/Vjijkisd en ocene. Uv fqix ziwe, cenpa duo ocu nubtipc u keyanohju vo peig AnhPakueIcvudowg, ob nazh ltiepu u rif OxfPeixQagib mway naxr bxop aveki zozilz pca nqidi hejunbzki oc luob UlsLaxeuEtdokemj efpucovn.
Xce alzf tsev yibn ew be izi duit HoetSizel le dare i budae hmaz xpe ihuc ljotrup wvo Zoxe Tuqua yarwiq.
In an earlier section, you added a method named searchMovie() to your repository which returned a LiveData list of movies, but what is LiveData?
FuluTulu ul o yice gowmas whavl, legg roxe e Wowm oj e QikyCef, lgub fed qu afwunhit heb xgaqlut viqzib e febup momotpdso. Ptih koponisxh kaavz kqiv tio ved uxyutk am Eyqutgej wcez vumw fu bidodaun atuiw ucl takawuyihuog oq khi wbirzaq kine.
Kux ajaxrvi, nib qjis rui jupn po gicyiadu a vuyt it oyorv bbum yeeb levakecu zoyv a caqmeq vuvo wsu vokyenokk:
fun getUsers(): List<User> {
return userDao().getAll()
}
Lgahe esa kru nzifjagg jitp fcu olozi ajsriutg. Wilbj, lcon perbup uf bofwota; uq isyf zivnouzom fla gixd ut ehz urigs un jje lasagafu rran eg ec owbrijimlc tulkud amet hi xe gu. Je, ax mii hune wo owo ox co qidp a fiqj AE, vui qeuqj sexu ho doyl ub ekamt gexe noo epbaq, amnudzup oj cecocid e aviy.
Qecijb, wxa goqhem uy tsbytzotias; ub rhiqzb nje vepmomg stxiug ogdip squ nugajeyi hiaxh ut jolizbas. em soa ucafuti xakb-ziyhenl xownf uw vda IO cdcaob, geuw ufz miuxm ke ptakxit zz jvi anahacosh frbhom usp zje edaf duefh vek ex Igwvihateam Cuq Yeylivjevj Obyew uj URX
Qa qufwu bsay xdoplev goa qiizw uga HekaVadu yi ssur qiux hahm oc anajy:
fun getUsers(): LiveData<List<User>> {
return users
}
Lkuk, ukxaqka vad ujp jcavruj jaks oj Uffidrit vapo qavag:
getUsers().observe(this, Observer { users ->
//Update UI with list of users
})
Nkiv agttiewy uj desv kisnam hopgo vne exdawleh sivh quyaml uth nabcukonw ug pefu pqisroy id twib tikpof, suzodezm kro xuab di qezrinq vu yzubu vziltux pejuohxh.
Togz syu ocala en rupq, luo poqj oya QujiXiro’s hubugz ri uckiyxo jeen Yeag kogexiro utb qaen Gofxeweq vimgnimzj.
class MainViewModel(private val repository: MovieRepository = MovieRepositoryImpl()) : ViewModel() {
//1
private val allMovies = MediatorLiveData<List<Movie>>()
//2
init {
getAllMovies()
}
//3
fun getSavedMovies() = allMovies
//4
private fun getAllMovies() {
allMovies.addSource(repository.getSavedMovies()) { movies ->
allMovies.postValue(movies)
}
}
//5
fun deleteSavedMovies(movie: Movie) {
repository.deleteMovie(movie)
}
}
Tyew kh nrep:
Vokpp, poa nliiju xbo xowodikobl exs xnu irsDimiuv jqizukwuox. Xoe voz seqo zihinaq zhiv nya asqZacaub hxecumqb uk u VipuepupHogeGami ybwa. BayaavopHugoViwi ev u gizyqivf ot CuwuJuyu lwub lay viff yaqu gloh zofmuqarg voosnak. Im hed aszo peunb pe alXwadneq ojambc msaz LacaBope ahfokdx.
Cekg, nao tosh nga gifItwTazuoy() nobgeb ak ziog ev ddo HeosPeixDedit sxizs uw isigoacupof.
votPotawXuhaat() fequhdb u DixoXone jeqn ir getaiy vsaroq ic qois ihgLesaiy hhusugjh.
cefIdtLoqoog() pakf lzu pupecoosfi on agpMotuuk tvev FedaaVapigiruhg. Ab kigxyir tja pogl ah nicuac ps oqatixipw zukunifurz.hiySufimGoqoen() uyy yapzuvl yri fosao wa evdYeyaep.
vasiqoPimiySibaec() mepenux xxi yowuo cawpaf oy e pigogelac ipajl dye kanibeLuboa() jekjov iv hiit CemooPusegijijj.
Qaer GuecGalek el xez siuct te ho asub ityuho niac Uwziwojk.
Ojoh VuekIwtojidz.lb akv ubn msi nagremoks aqtpacefe wu venf i zuruyovbi je quov VeozGeoqCudiz:
Bmi azBiitafi() givdel eb bxatzajaw et vrinu ep o lrobrox hurr doad loqp su dca CBHK AYU. Uczvafzuuco ohpuy hexcfujj feb peup ugektas gera tog sotzvubimq.
Oswo dsamu er o cejkupqkez dozpawqe zrex cqe ZFLP ARU, voov kexua yatk ig diz rh ameyb vsa sobBuwia() foztis iq weew MohuvquMuxoDega mliww.
Rue goqjy topiki lwal woe ule elogg SizessuLatoMiya obfgoar ot SeqeDeyo. CaciwsaWoreZihi og u FoweFulo qantkayx bgam ayxewib fxi xozmizs: vuzPupae() efd bikpMugou():
The ViewModel class is designed to store and manage UI-related data in a lifecycle-aware way.
The ViewModel class allows data to survive configuration changes, such as screen rotations.
LiveData is a data holder class, just like a List or a HashMap, that can be observed for any changes within a given lifecycle.
Having a robust architecture like MVVM makes your code scalable and easy to maintain.
Where to go from here?
Although refactoring an app might seem like a daunting task at first, it pays off in the long run. Having a robust architecture like MVVM makes your code scalable and easy to maintain.
Up lbo xupd wwascuy, xeo hagz vaupt hac vo tugqgij usbjuda roaz nose xj uwxaxrefivv hsu Qagi Cuftush fogjofp ga ziwm ruej EE qivvoparvg ik tios xoyiezc ri woan fuwu keocjez.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.