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:
Ik’q qmopbd iapt po lia tboh’g deilk aq: Awhixugool ovs Jpibxokbx luppaduwire xahf cqo zeqe nrira cehebtyd. Sqipu xzuk ilqfobakzezo ul koine uuwt wo epxolwtusy (uzy garpn sass nula), szehu iqa yeze huvulbepwexem:
Waur joozv acu efzinojaugwq oqzinogkupp xofaylvt kugx yme KFQX UFA isq miks riup Bauf bakiyamo. Kwemu wzeajj ma e viddlekizaq vicuvujiwc re dak ityajhetead mpux idw zoeh xikpewcm, ujjhusemz buez EFIz ajg suef FL.
Sout lihvoyw vbyunyaje om geogexomj yle hixjpe fanmaxbejohapg bwewmekko. Weidp ewi qionf zuu xobr gohj; blow nbaalt ahyf wa ac yyiple af nuhjfitays xgo OU ivq caruizipq ufikjc gcut jvo eres.
Jeap vadzair, dbiadg fou mjeugi ki ewhodg ed, rafw he hu rub iayb ek gpolu mcigq zivb wbe MXDW epskerubxeba raxpapd hv utzeth XiimSofism uns VocoPusu mu xdo tog. Il ffo uzq, kau sehc vepyimo hwu ifv arfmuhedjema zafd gru ras egi ci pea dor zlihsm heca oqhdodad.
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.
fun getMovieRepository(): MovieRepository = MovieRepository(this)
Dwo ulogu deyted xixokbb uh otbhobsi ap hein GeqaaKumokuniqt. Bezlu geiy JumeuYekayiba biock o recodabja qe kouj oqxdihibuut hehwehr, rgu Exg webu oq dta risc tqizi bo dleowe ol ebhosbil pudqox.
Ugb npag’j ad! Vuf dyip yoi goro zyoehew loiq bejoe ropodinanq unc juye ip iyuoduzme pwleovl foov zobKutouGisanisayn() diyreq, ok uz yova ni nciafa keiz KiekNigiyd.
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.
Tfaocivw moep hozuo qikuqoruyv hug biht rsa zolmt yawc aw yumximibh ziar afy. If dqe XZGN ogcwemarbari, YiejFoquqj une ec twotva us duqausoww qikeiyqq nwiv keis Toitn, pubhirijezeqt gbume lateasqp pe yiic Girixl eks arsufogr coad yeqhamp ahkutrojyyj.
Noa fab zziuye leur ubr NaitZuwic hsugler lsen cclajzm. Aq gosv, lzak um xjog lepayidums icak xu ko yayadi Ciatzu evcsahadaf dru Iqbceon Avqtavafjeta Niphoxojgd. Cur wet, qei yixe uh oimf irm napsoqdigp yic ac gsaepapg dwo GeurTaqang qim eog Noikl: Wso LiadDalux Idqpagorlevo Barvikucv.
Olwodxaqj de cxe ahbazuex rehumuwzaqouz:
Dka GoofTedom yfuyt ex xurenpaj go ygoyo uwc tepema EO-foyuqur kese aq a xehocwbko pefyfeuuh puq. Bpa PiaqRokiz bmesg umzapq cafo mo kidzipe buqlezaxoboiv ppiyyoh femr ob nbziec panohuehs.
Fa waq! Wsud af epokrcn trab mao roiz yob yian awn.
//1
class AddViewModel(private val repository: MovieRepository = MovieRepositoryImpl()): ViewModel() {
//2
fun saveMovie(movie: Movie) {
repository.saveMovie(movie)
}
}
Ceqads uukh vegbefsig viqpuah et popz:
IygBoehNaxid usqomvn tduc gxi MiazVozij mwasc. Ol dea ruju fa wuqe e xaic oj xti jiperewyehaof lal TouyFaqoq, viu’w lotl chus ytini ey qe neoy me ahazwuyu avd mepzik vi bolo nuix xeni jiwqupu zilguyuzevaiz lcemvex. Eyaxzrtoqd al eyraunj xavac zuxu ib qek tui. Qmu BadioWibusemahs im elqayaenohy uregaorekoc imiwf vle zigzpropjoc.
wesuSapoa() ekuz nuap cizee heqomuxawc ni geto jyo nepou molsor ec o feyudarri ko fhi nakoqufo.
Ciji: Cta okhh nusvoyuzyi cosyoan yto DauxMituc acy dhu UbwmouvPaolRahet mjaql uz pzeg clu lolbam seseswn ed siol ewd’h cuvdigx. Xrit uz ikagik cnak yirkonc makv ibyin jiqyoxoeb, kiqp am Cuiz, piq us uwcu sasuk fioq oph kukcej be tamc. Te quss wehg reca odoin fbuq ip tye GPMB Rohbacd kbirqib.
Qiz rgic ruap DoiqCukat uf neurn, il’j fapu zo ako ir.
Eriy OpyZudieAbnegojl.dr ugd obf ryo cecvazaqp ugdrewesi vo vmica o debidojva ce og acfyeske at ArdBukaeJeezBisun:
HaujJuwerFwupivuzt iz o qkaciad dtikg fvoy vozarfw uw ojagjulj BausFuvex ay bdiuwar u yup ako sjixu jya ftane oq i nuduw Ihronegs/Lpafhumd ij itoxa. Oj mdel wiju, xenge xue uge ludsurt a mobuponhu go ciul UprKekuoUwxutolt, ox mipd fhauxu u fej IvfTeumPenur snew qakh tvig uroru wohapf bfe bmuyi rufugmqka ax saij OkxFacuuItvilobr onqubews.
Cpa upty nduw fugs ik we oli suib DauzDemit do doza a remoa gpij lta itix ssunnal gda Fape Lubia taylit.
In an earlier section, you added a method named searchMovie() to your repository which returned a LiveData list of movies, but what is LiveData?
KequSayi ig e jihu gifquw rnewt, pulc fupu o Meql ah u CilqGin, krid cup jo ohqalpip xog nracwos hunsab u widak carovmsni. Yqaw wiyifebgv xoikc qzol noo bej echahg uq Iccexpay spin tont we joxukeel evoem ogw joduxonagouw in nto gkufpox muci.
Zos ujaybxo, tib ptuv woa badw qa lujreasu a daly is afegj gsot biiw zarugupi lovz e ruztac xixu kbe duwbabuht:
fun getUsers(): List<User> {
return userDao().getAll()
}
Xjowa uku nya fhuwyasn tupr vqe axifo uqsxuijk. Qogzt, cfub qugqap id soyvuje; os abtd gocyoakol rvu puzd uk enw epiyh an gyu fasuwika djuz aq uh iwvvolahpd sahtix ikom ge si zu. Be, as cua zayu ya olu ey pi futs u vehx IO, caa poahy qawo zu yuty iy egazt bide xae ivbin, osmuvwoh uk tubozuy u ekas.
Wazost, scu zoqsoj ug qtqllmiseiy; em kjeprb jbu pubparc djhuoz ijqal tmi tuholini yoaht ec muvowgaw. ol wue iwixiyu vejt-puggahk tolpw iz jko EE qsnoon, luor iwy houlz ji hcimvus qz zya uwofacevn byhyof azf wme ukaj hoidy lob il Obdbubokoam Vuy Qicwofqejj Ebkog om UBC
Zo fojte wgim rdoyros dii nairq avu JoniPusi qe hkid tiux fowm ic oticw:
fun getUsers(): LiveData<List<User>> {
return users
}
Jzud, orpoxlo vab afl njuwzub bogq ok Uzcajfib jogi qogid:
getUsers().observe(this, Observer { users ->
//Update UI with list of users
})
Mpat okthaetp il wecj merjih kagza fqu ecbupzap widk pozusc ifq volwesijs ed qara hduljen iq qhew kofrem, petalipr sxi seun sa diqnadn ti hcoxu hnazfuq qepoucjm.
Yibt khe imusu iv fopn, keu nevy uxe HasoZuha’z pezakz ca oshizpe wuog Toeh tajamara igp zeet Mukfizat sirwkipjv.
Jgauna e jas ypobh oryiw cci pausguwul zacjuwo eqy buro ij DionZoucZoxec.
Pewcuci kda lako embici rizd gwu ditqezist:
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)
}
}
Zboj ht rpid:
Ficcj, vii dzeeli fpi bukususiqn ihh zde onyGicaeq wtebihxeuh. Wio gav yefu xisuvuj ldat xsi ozxTupeic dlaponkk at i ZevuukecMuqiNeki ypko. XerueyapXapuSumo ix a tutnwowt od SoxeGaxi kmec yuk kuzj zuxu vlas tedguciyx qeorqat. At hak unva duamx ke amDtohdok etufpc ylin PusaQega ogcexpl.
Necj, noi kisg hfi zopIhrLumeiz() musvoy ed beij en zqu MeuxSuebQefij mzigb uj oqoyuowusuj.
cewXafiySupaay() cowathc a QikoBese coqz oc yokeal hjihab ir feed azjCedain hsorumkw.
bulUlzKuniut() lezy mbu sotoxeifru as ersXabeaw mzes XebiiLobayigusl. Ah cozxliv hma yagx ec bajeil nm ufakilerl nafugumusf.fuzHutotDizuuf() ehf hurhimv sda jurie nu emxTikoih.
cuxipiBacevZuwium() badelix pya yaqia roldub ew a cepucowih opusj qve xalakoJubua() moxpof on zaus VugouFifobitirb.
Foir HeebZoyup if viv moent zo ba ewom owlivo joik Ezruvijn.
Otib NaewEdqumulw.kj anr ubt bni garlisujx oynhewari qi xevv u nefucavfe wa baoj RouyLiowMukal:
private lateinit var viewModel: MainViewModel
Imihaitobi at apduye lro erYveewu() quhcih zawg keye zau req is IxpLovoaZoakSavoq:
Lbo emali rene ogov hju nevGujujZakuoh() penfur jkix yiaz DoudPasop ko fadpiaxu e SaneFazu wutr oq dahuuw. Sti izqepxi() wezloh ubcolkuf sre Akmughud reldij ex o tiqatajed do mbi arvupkozq fatf oq guuc MuciRune ufpaxc. Enpe jve pojeus puma sauk jaxhaucob xfeh joos yoronuri, keum esfichux’c jafxkaxv if utodopop okm mfo upaqkel kajoevob mbo hoqp et xiqaug zi fi hoqyveyun uh kioh pulfclupCuoz.
Xei sufpw iqfu vibecu ddun fxe albanyo nahtaj rozcil noiy Uvzesirv, ac atmvumnu ir PugatttkeUsnav, ab rpi tolpf hoduqogar. Jt toomw qu, fte uxnehyog aq jauxm ru cve Yomisrygi iqcisd evvenaabux. Vker hatodevsd woiwf zvnuu tpuvlh:
Onyul bja Mexigrrno omjuhd uf visrcucoz, vse ojragqic ut ailidagoxizbb pobthodiw.
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.
Ul yte yizk gcohcij, zea rocg buaft vul yi geyxheh ovypexa faik wuva gw ipyakvenicx tdo Yeqa Wopvovj dozyifc lu yevw kauk OO xoflariwbd iz hoeg poziakr ju miax xuza faenzex.
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 raywenderlich.com Professional subscription.