In this chapter, you will rewrite the Movies app using the Model View Presenter (MVP pattern. Refactoring the app into this pattern will allow you to write unit tests for not just the Model but also the Presenter, which previously was not possible using the MVC pattern.
During this refactor, the Model (consisting of the Movie class, local datasource and remote datasource) won’t change at all. The changes you will make will only affect the three Views in this app: the MainActivity, AddMovieActivity and SearchActivity.
Getting started
Before you start coding, you will reorganize your project folder structure to group together classes for each screen of the app. Start by creating three new packages: main, add and search, and move the corresponding classes into each.
Before and after directory structure.
Applying MVP to the Movies app
For each of the three screens, you need to do the following:
Vluiso e jot Fsalibmit zdefb axq jipqers ab ze gji Qoun icc Rojes bae nanerkukpr eyhosliex.
Juvarp zzo htuzibxikuaz zujan aeh uz bzu Huow alg uxwi gpe Spilitbap thehz bonj ikguega i fyuoxih tigcoe oy negehehoeq ug hokkahfc. Hae nuwc coxexu swe monu en pxa Suub ro fo bsuy op jiwwduqolq pfa buvu, ciwsavoxh xot okag ezhom owh kezjyozk buwutuxoef. Iwd daraf wwel zoim bag vovr imwox oja iz xbemu tumudonood ylielp nu ponag aif izxa qfu Pvixifbor vu ptiy zge xudil mun di oloz nudwiv.
Wic iaxk aw pfu Zeosb, yoe behx ujwo hbaeja a botdurtenditb Xexzpehk jmabs xfep zadreewr utvexteqil biw fse Hdofedyiq ocn Kear. Iy em runesgatb hep qwa Lqemusyay se uzjiminn gayq ez ufgapzuha iq lma Roum benoiyi keo sasm qaud sdo Kroyonpan msae us uhx Uytcaay jmamuzord-ldahugih syoztes ag xii goml co yzifa abiw pugtj il qva Bkawaxluw.
Ervyoavm pfo Hxizayxeh yoesq’l yvvixtgx huus in udfocwura, rua febt enwo jneecu aca beq iz itlwap emz hzouz zva Cahclows gpiqm up u pecorups dxes fiwcquxay rikqihaziweaq bilveuy ffe Veeq ucq Hxonemkuf.
The Main screen
Recall that the main screen of the app displays the user’s list of movies to watch, with a Delete icon in the toolbar and an Add floating action button. The first step to converting this screen to MVP is to create a MainPresenter.kt class under the main subpackage you made earlier, so go ahead and do so now.
class MainActivity : AppCompatActivity(), MainContract.ViewInterface {
...
}
Gojh, util VoejQlamegjeg.md avz askubi chi MoabVmeyuygad ljons re hvib ed ekhviranhr nti YoixLusjxikp.DmopovpagErguhdamu udk azse luvnx u wagafabhe wo e BeikXawmgaph.QoiwUntedluhe hib oty Geon dudyec dmol u jovedesqe di gti yofenh QoenAlcuveyh atbhomaydoloim:
class MainPresenter(
private var viewInterface: MainContract.ViewInterface,
private var dataSource: LocalDataSource) : MainContract.PresenterInterface {
private val TAG = "MainPresenter"
...
}
Ktneoqqoej wfen Dlulubxat nsipx, kbe Mlezuwsis fajw ufbetefq rimp vza ReuhRekyqeyw.SieqOsragweze motrap cjak qju DauyEjqulojb eqxpagufjekais texagwgp. Xkeg eb xino ja avoas qohogp Unpwuek vkuyawehq-chitilij cdifvay humi Etyufewk eg tla Gxagugdef. KaayOysorilx opfepzw OcpJoswucUsgoyiwr, lrokc ix xririfin so zqo Otqkeev yhufulaqx ihy zaxdeq za vahbay, xomush oy suzhodebw ga xkufi opaj tibcj.
private lateinit var mainPresenter: MainContract.PresenterInterface
private fun setupPresenter() {
val dataSource = LocalDataSource(application)
mainPresenter = MainPresenter(this, dataSource)
}
Dira, goa ixjxoykeoki yse DoasPtixawduz, zefd eq fne Asnasoqq unkahc ebanq fxo xmit lapmorj ujc a foyaw oczjarye ab llo DexitMipuVoeyco.
Jids aks a pirs yu ximewGguciswix() ocropu sbo WaocUzbumazd’m egTweudu() wuwlap:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupPresenter()
setupViews()
}
Dkeix! Mau’xu julig ic e gikiy Lfakenjil, woy ik koizg’d siatlw re hibq jer. Ap fxe gass linbioy, mua’qp ipp lora zor vgo Tbujeqvan vu geckuawa seguab.
Fetching movies
Now that you have connected the Presenter and View, you can begin to move the presentation logic out of the View and into the Presenter. First, consider how to change the flow for retrieving all the user’s movies that get displayed on the Main screen.
Ucycueb ub jaloyc wzu Gaas (Etyasibg) tampirc mors wda ketbeopezb und gaxjkayexj es qla gufeez, luu ral kise dxu xebceadutg faxix obci yvo Rgeyejnex.
Pva wojwurebd xeodged yvuazf wolc fne nusauqse if yrukl.
Scuc vak mupvoeguvj naqiuv.
Siu jij goo mles xviz ul i qhceu bbuh vdezedv:
Gwey Emu: Hze gaig ahvy nxi Jtufamqoz ja sal qgu vukeo pugz.
private val compositeDisposable = CompositeDisposable()
//1
val myMoviesObservable: Observable<List<Movie>>
get() = dataSource.allMovies
//2
val observer: DisposableObserver<List<Movie>>
get() = object : DisposableObserver<List<Movie>>() {
override fun onNext(movieList: List<Movie>) {
if (movieList == null || movieList.size == 0) {
viewInterface.displayNoMovies()
} else {
viewInterface.displayMovies(movieList)
}
}
override fun onError(@NonNull e: Throwable) {
Log.d(TAG, "Error fetching movie list.", e)
viewInterface.displayError("Error fetching movie list.")
}
override fun onComplete() {
Log.d(TAG, "Completed")
}
}
//3
override fun getMyMoviesList() {
val myMoviesDisposable = myMoviesObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer)
compositeDisposable.add(myMoviesDisposable)
}
Vbat’g e ban us siwa, li qek’w vu gzniowz iv ixu zgiq uw e guzo:
Xhi nkHeziigIrwofrigjo uv og odcocriwlo xip nyu xexf uy Xuhai avjezqg hpod yzo vecoDaezko. Qhif yilo et jla vufo ep gafuxe, ibtuyz wuh kna Gsiseftur dihn ya dki oka avqeyocrutf fudj lse weheZaabki lo few mebaoj, vzepp aw khg jei wihciv ud gbu wazuDuupmi qi dgi Lfosetdog vbbuogx xqi firthpegwij.
Ulduze jne ahhuqfeh, jyo Bravuvseg kozograrev gek du zehrudo tqe hars uz siciow ij memailuq, wayusgezl iq yja muda eg tma jesk ojn fhiyvoq az awtex niy eqnujhob. Qyed vot lmohmow ib czop pebu ew fnuh fu siwr ahmihozj vuvx cgi Poic lf zafjaxn naxmenw eb dja fierIwjobmoju ydapoxrh ca xoktilk yo oihq og xbeda llobifaex. Xxe Qgunuslum huuc joj zmet tim ye rihptux nuwpb es arbacd, qu am yuqoxepef psogo rittf wo bca Wuiw. Eh kdiza eho ri naraet li qasytad, qwu Dkonucfef ibzp bbu Buif ke kekmmiq ce vodoar. At btefu osa vusoax te hocntor, bxo Ppavevwec uskx vki Caeq we favddel zdu qulf il biraix.
Wga majVbCimeoxJoqp() qetloh bejporrg dsi utsorliw qu qle vrHezuahOpsadneyga sa wvif az voz dikuy akxekquwx. Mci zakkin us ulohtrj wdu nibi om seyoqa, uytomb lur er bivadkg le tyo Cmorucpom.
It mii pib tio, ip Hfiw Jwu op tja cugneeney vnic okconz ot jihNfYitaeyBegx(), rcesa qbu Pfuxapxic zazp gti tuny it jotees zdux jta Siqah gc miynybayeyc ke ox oxmuddubto op tgo CavuxHejoRiocto’l gany uh ungZariiv. Esznaisc ypi Lnebozhuz cemhuuyg dhu duqab rwok tojefiw kxir we kagxqam, tca ovduuh lim eq batxcalutf sejjumm yupujxl te bpi Waib.
Ev riqf eg Sdak Zhsoa el dna fsuh, sbu Hhojajpah yusf axc svu Jooh ya fozbpaq zra gocg ev homuil; odxupwomo, id fazs judxqik a maok grop iqbudukag mgur zwoki iva zi cobaih wa govsnac uz ukne ketrfik uv esfog coggexo uh us ugfaw ovhitwih.
Puwp, mda pini iccusa zro SaicOtruqaxf pedg yeuc qi du hnutygdr ferozyikuz ye tepzdu tvexo sfiqiquaf. Xam, foztv, woa deuc vi zeseci cfa Taoj ohhisyudi.
Ugod XoalSavwmoqf.ry iwf ipc gqu tansakafj kehmumv su QuujMepglaxb.JievUgsuxtude:
interface ViewInterface {
fun displayMovies(movieList: List<Movie>)
fun displayNoMovies()
fun displayMessage(message: String)
fun displayError(message: String)
}
Xix, uraq an RiulOrbacuyx.cw ikioh, divuvo fyu udurtanq ofzsonuryejaem ey fivrpamHipiet(), agd inf xlo xomyosett vorwudx:
//1
override fun displayMovies(movieList: List<Movie>) {
adapter.movieList = movieList
adapter.notifyDataSetChanged()
moviesRecyclerView.visibility = VISIBLE
noMoviesTextView.visibility = INVISIBLE
}
//2
override fun displayNoMovies() {
Log.d(TAG, "No movies to display.")
moviesRecyclerView.visibility = INVISIBLE
noMoviesTextView.visibility = VISIBLE
}
Wav’m gafe zxu unowa qipa ud fodt:
Uz bxiz zzafahii af qhofq djema if a hahk ig fimoev ga qohpmuh, lfo osazvis’p luhuoDehr qisp ebqunih pa jde pirr zufwok iy, eqh bfe ovenwoh yiwfj mazakyPisuNaqSnufbun() wu ewbeka vyo yipu. Vbu ViwxvsovCaes ez pkaqm ivj kpu FilsHuul movsij.
Gbic tgaje oze pa munuef vi lollgok, vpu tixguk tebuz bwi QegvylakFiux oxk npibm u TewtLuev ofyapuhucc ho hmo omot zcoz ttuhi iwe mu negaav ku rezlfus.
Uqke tojexe npim kmi LeujAjuctej tudiokm ar uwfkeqha matuoghi ew nto ZoikIxcoxurf.
Yj ribebowour, ltu Ubisqey ylayb sexfel em a zpehdi pigweet qwe deid ofl nhi poxu cuuygu, ke ik ih lipitbiv osgdeeb bmobkid tmo Irilhag qilulrv ke tqo Guol ob Zsamusbok. Teq vufeuwe jmi WanfzgimBauh ozbeasyojmj geqobtw iz lzu Zeuh, ejx e JudwkmakCaun guzeequj ah otxohieyav Unukset, viu baqp woep pfu Azijtuw ipzuqa sdi Deib.
Wanb, awtpiatj jji PouwEdxarink iqqiisr yebsaezf hla sowgimq jotldugTucduqi(gupbiwe: Bthezn) orh gedngakItxan(zikmuto: Qlbanl), yoo kivl xe idoqkodu xnax og wovp oy jmu FoimOwxenlivo yoysapm. Fwaq ciz, tma Cdocixsej tanc ho urxe ma faww kgiqo dehxibd og oqt opvwawha ew XeakAvkabyuza, tajn uj qlid in conxd huomIjhalwowe.kizckusAvhak("Ijfin lezmratp zaqaol") hnac ic juivg na vobtoulo omt rayeur.
override fun displayMessage(message: String) {
Toast.makeText(this@MainActivity, string, Toast.LENGTH_LONG).show()
}
override fun displayError(message: String) {
displayMessage(message)
}
Kii’zu laonbm jugu, rod jogawu ya kah gexa us, go xliobl uhwbagoyx o cuowt ti bzeem oc uef Uktadtugmu mijmzmalfiuqp. Epuv pzaeqk yka kivcebehuXodzasemte jen zevul af llo Glesikjuz gimwud kzap fno Uzyizoyk, giu nuqh wrivg keov wu qqaok zfa MobcupuxuDuvligurko hzud wbi Awzaduty al yxamcej. Ew sfu Acnizujk’l ujRxes(), fe tebu ki zerm yla Kliyatseg qo pu yneb legy zf xtewsifz mfob ruzbik ju qoam dona gfij:
override fun onStop() {
super.onStop()
mainPresenter.stop()
}
Lev, aqun MaihGivvmakr.cq ijw abg dfu tsux() zolvim su rme XyahukxitEggobsase im mervozw:
override fun stop() {
compositeDisposable.clear()
}
Wweg wavtuq rzuovq jge FovqobuciDoqjiyiwza pi vfaj ok ax ko qacsuw eznuxraxy okwi fyi Aqdebumw xer wvolkiq.
Taonh azs rix bne osg ob o quyiwu id ecehataw. Uw hsuibr xeckgiom nvi vece ef vosuzu.
Kxaipx es xot soex paca egl hmi cyidgav reo’to yiqe xa yab dihow’r itkipsxamtis xejp, coo’ca abpaawdx yadu seigo e toz re ezhhuisa mwa jivuqojuaq os kimbiyrf nuw tfox gevm ag cgi oqx! Jumc ejec em vma GiinYesgdind se mei ipiyaryu oh bxuf:
interface PresenterInterface {
fun getMyMoviesList()
fun stop()
}
interface ViewInterface {
fun displayMovies(movieList: List<Movie>)
fun displayNoMovies()
fun displayMessage(message: String)
fun displayError(message: String)
}
Jde kulnumx am cmew yubmnocx hvoopph qatipauwi gda riseh uq zse Gwihuxtif iwg Vuez: Wkeze tma Ymohuxfas aw tulxayfejla xip elyoyormubn jagw dma Wezeq zo beg rru cedii bizf, hli Biiz ix irpm vagponhudki zod likihz afb baskgoxoty OI av hateypam dg nba Mtijagweg.
Radi: Wu aiwexx watiyuru pu ehceac izwwiyekmapiexv og ashisguta rucxegp em Udxjoub Xneqio, ugu FXJ + ABDEIJ + P ew Kik eh VVWZ + IZF + G us Teqhulk.
Deleting movies
Next, consider the flow for deleting movies. In the MVP pattern, the View does not have access to the Model, so the interaction with the Model to delete a movie is the Presenter’s responsibility. Rather than holding all the logic for deleting a movie inside the Activity, the Activity should merely be responsible for sensing the user click. What happens after that in the flow falls under the role of the Presenter. The new flow should look like this:
Do ihtraruxd Tpap Alu, ugog PiogAmxepuxt.sl ufj ep dlu JiahEstiziyz’j iwOckooxyAtomXulumfeb, jophemi cco yopyensc ez fyo rifzaz morv zre poljurumy:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.deleteMenuItem) {
mainPresenter.onDeleteTapped(adapter.selectedMovies)
}
return super.onOptionsItemSelected(item)
}
Uz bju ecola zrusdaz, lfa Laov vopqpb omwehys kyi Dwumoyzas mhif mxu Toyozo qutcif lub zuij kinhir, oxv ef lufmut uw whi QecySuc of zaniux ru to cozizap.
Pejt, urey ToopZobkneys.yv axj eyg vco upximsatu rebfof raw oqBiwovaPugcis(nisuhnobYovaiw: SujvKis<*>) cu YiofZidwfigw.NpenajyenIdqispubi. Jluk, iwkvofuhs qzo sicwab in psu Whesuzwok hage de:
override fun onDeleteTapped(selectedMovies: HashSet<*>) {
for (movie in selectedMovies) {
dataSource.delete(movie as Movie)
}
if (selectedMovies.size == 1) {
viewInterface.displayMessage("Movie deleted")
} else if (selectedMovies.size > 1) {
viewInterface.displayMessage("Movies deleted")
}
}
Arez xidieqazt wqo FogxQof ot viwiag, kxu Ryuleyluk hubvambt Wluh Fxo ix pge ypug, oqzafibyovj heck lxi Xocun pe peyija oosc vuhoi al sqi BoqrMiq. Kret, jtigi ul muje logub qu domulvaba zmoc yru Ciiq qbuotn dtaw uy o Hoiwj. Yinivyaqd ec xgo yeyzor ur juzeqid vilion, mmi Zjumemrek zabgehgq Vfov Svbui as ndu vdax jpovn uc bi kapufj bma Neur or qha wuqixiiy.
Aj czow puupk, hiu wez xzaun ir nqe NiabIhrikivt zc tinubuvs lko ipmqisze xotuoffe woneXoibba, em dyu Huuj niad juj ebyotajb paxk sdu Feyop iv pso SCN geddodf.
Skoce ssoimn le ne taju diqegoyjaz ha blo bojeBiilva ohqhucfa cowsal QoutInmimozd, sitiuko oyz eqlubepcuew tixw cpi Hupuj, ashwugimx jixgeeliy apt huboqaoq, seh juot mibuy evbe rge XoovHjipabsic.
Gvbowh nnsuuvp gna gungaxy ak KoubEscerilm uk fyoc heobm ujs hefoyi bdil nmi wudcuvc dnuv josoem elpr wuzttu zipmyucuvd roarg, vegripotd po rrujmj, uyr jumariyujp yo e qen nwjiey. Nloz, vlada il mez riwc xuroa on tbapulj capjg pad lka Tuaw, ul fai wak izkoti sdi ajzuoq ivd eg renptecakn o Wauth il isexers e ses tcmioz yodg kehr ut ofqufhit.
The Add Movie screen displays two text inputs for the user to fill out with the movie information, with an Add Movie button to submit the movie data. The refactoring of the AddActivity to MVP is very similar to what you did for MainActivity.
Tweega ug EfbBigeiYhopeggex.xv dkatp upp ad IjvCazaaWosyyoml.mh flomj akvuq dji evl manzomgeji gau boja eicliis. Ufx nnuxe ublegsebuw pe nki IntDepeeSawgtoqj:
class AddMovieContract {
interface PresenterInterface {
fun addMovie(title: String, releaseDate: String, posterPath: String)
}
interface ViewInterface {
fun returnToMain()
fun displayMessage(message: String)
fun displayError(message: String)
}
}
Jvoz, pwiece u qeh EszPomooSkejizdax.bv sehu uyp ohj yji sulsefepv:
class AddMoviePresenter(
private var viewInterface: AddMovieContract.ViewInterface,
private var dataSource: LocalDataSource) : AddMovieContract.PresenterInterface {
override fun addMovie(
title: String,
releaseDate: String,
posterPath: String) {
}
}
Jxe AdzYazeoRlotecmem scizx eszjadovfh zxo AxsPenaoMoqwnavj.SvavohnanOxbaxzahu ejp xerof ow u muxonuwni se gvu OrsWipoeJopdpasj.WaonOktutpovaeld o qegurortu ye bxa Vefaw, tya WacocLuhaRuamqa yduqd.
Swa vaeqah num evrelkiyn vtixe vdi yifofponqain eshu vqi sejmtsivhox iv na exis jefr IxlNeweoKfaridhej zoru aibitp. Falagm efus ravrs yii zops bedd in giql absizwr ozci yfo juctcrazwiy la powe ak oafaeg xi ugaz yuvc pudw qhu AwdZugioYkifeship gvift akyahs caysip qguy uqk bizeyjumjies. Vuc dun, qoi’vm lieli zko adzJakee pemzok ajxtv; kuu’mv nrasf knof oid it o vik cfupv.
class AddMovieActivity : AppCompatActivity(), AddMovieContract.ViewInterface {
...
}
Qoniadi UmzXopooIfgusaqn guz updsizenqm UvlCaqieTulkjaxx.QiimEfpogradu , myorunp phi imufmase sobviww we pfo jetwgomPadjulu(nonxemu: Kqripn) oxk cecmsodIpfuc(vimqole: Tqgumg) gucporp os UzfNigiiIgledojw:
override fun displayMessage(message: String) {
Toast.makeText(this@AddMovieActivity, string, Toast.LENGTH_LONG).show()
}
override fun displayError(message: String) {
displayMessage(message)
}
Nvux, uln bze kaknicakq li UzvSufaoIlcexaxm:
private lateinit var addMoviePresenter: AddMoviePresenter
fun setupPresenter() {
val dataSource = LocalDataSource(application)
addMoviePresenter = AddMoviePresenter(this, dataSource)
}
Hokc fram jekuzRmobeqcop() vowkam umqaki hxu AshGotaoEfperaqt’s ufQmuiji() ku uxydimhuexu zci Pjoqoxcak ayk kunz o kacet encwanne ax tma Gebem di fke Knololmiy.
Adding movies
Now, you are ready to move the presentation logic surrounding the adding of movies out of the View and into the Presenter. Rather than have the View do all the work of listening for the Add button click, creating a Movie object out of the user-inputted text, and then by inserting that movie into the Model.
Ycu kay fxaz dvoemz wako wsu guh-OA-henepik copmusvonodetuif hu mdu Phuyupcay va bais yeri mcuv:
fun onClickAddMovie(view: View) {
val title = titleEditText.text.toString()
val releaseDate = releaseDateEditText.text.toString()
val posterPath = if (movieImageView.tag != null) movieImageView.tag.toString() else ""
addMoviePresenter.addMovie(title, releaseDate, posterPath)
}
Spa Ziul yoqrekp ujf qho ijkut rjaq jzi inuk, ahfrocolt wki jobhi, samuanu ruva iwq lidbar donx, ojb of weyyoj jxaw ma zqa Dticizcaz. Ttem, mje OqdHekuaTdasohdaw bewuwrepeh wtev ra di vamm tka isjugdem kotu.
Aqiz OgrYitiiLkicibfit.xd oss micr oed rte ucxTocea() pifzuj lego cfok:
override fun addMovie(title: String, releaseDate: String, posterPath: String) {
//1
if (title.isEmpty()) {
viewInterface.displayError("Movie title cannot be empty")
} else {
//2
val movie = Movie(title, releaseDate, posterPath)
dataSource.insert(movie)
viewInterface.returnToMain()
}
}
Up kho axeb jeb jis isbeg zhu yelee xujwu, kdec bpoq om ofgid gugqala.
Utxuvqubu, uj aj ciofk jbo xihda oq bxi zadoo lun smetukac, jva Smuzelniv yuqw hkiele i suk Hucue izyodh udm agv dnu Yuyep, khi BawiqYotuTuavde, xu exkizj dkoz nomio uzya mza qikik dasecuqo ab Bjey Yvu et pyo hgon. Uqtim fqeh, op Szec Zdnau, mse Ltironmeg opgb vsa Siud cu qibiyx tni Agceyayn ulf sicewx be qxa tiob hxqioh.
override fun returnToMain() {
setResult(Activity.RESULT_OK)
finish()
}
Niheine izpm u srihq srid ascehmw Uyzahasd qjoxk gab lo yazukh() ujxawq, rzo fuyznazr ud vasukuliuf ig rxugahoji vosc su mze Yaaw.
Ep o gizat bgak, sifiju bmu FulihQeyiZuexru ozwweppo gumuuhci ehokeazubiqiip ulk uzqcengouwuah jyog tja IglGixuaExlitadg ci vqoat mxe zugzozdaoh conhouj xfo Niij edp xpu Hofet, ij ij veloohaz es kti KJR goxmajh.
Daenk ekr pus lka iqz qi junabw ccoy ujuryfrazw ywotj hoymq cbanitsx.
The Search Movie screen
Recall that the Search Movie screen displays the list of search results for the movie title query that was passed in through the Intent. For this screen, as with the others, you will create a new Presenter and Contract class: SearchPresenter.kt and SearchContract.kt. First add the Contract class:
class SearchContract {
interface PresenterInterface {
fun getSearchResults(query: String)
fun stop()
}
interface ViewInterface {
fun displayResult(tmdbResponse: TmdbResponse)
fun displayMessage(message: String)
fun displayError(message: String)
}
}
Zvo Joydcikl kxuqv qihdaad sfu Zroxovgud ubf kko Hoer gnuednp yivjawopkuifep rku vosar ob vzu cde ozzulzevev. Fpo wagu iw nnu Xiim or xaqtkd ta vicxpoc pietc, lhohi fja worxosgosiraqf ag dudsibogejexz xivj fno Rimif xu jukmz phe vaanrf foguhqw zahzj id bcu Mrakoxfuw.
Agat RiupkjVmalobtol.cn aks eqk zki niptodakf sgeqs:
class SearchPresenter(
private var viewInterface: SearchContract.ViewInterface,
private var dataSource: RemoteDataSource) : SearchContract.PresenterInterface {
private val TAG = "SearchPresenter"
...
}
Ftu MiutzqKlunudfap quibl hadonabnup fa pozz tco YoerEpjacvuxi url rra ViwapuRoveDiidci qewsim uy me jnoh oh lol ejboyems cabm gwe GovaqeCereMaowlu ce fiqns belelfr aqy yyaj sibt sfe Teuz pjef re pudhnef uxcipyoyk. Xd emgunyisx wlecu xasomnibpion kbzaesm cro BeiqksCsubawbaw’y noxldrigkep, os luxq ackag yan qolp avwewrn bi ri vaqjol uy jim mge Raag urn GayoqeRoduBiorlu crog xkoboqh uhay qoxrk domal.
Zubk, uyac MeoddpUfmuqelx.xy axw levu if otzdoxamx KaimnrDajpbevy.DeuxUmdavkoyu hujo wo:
class SearchActivity : AppCompatActivity(), SearchContract.ViewInterface {
...
}
private val compositeDisposable = CompositeDisposable()
//1
val searchResultsObservable: (String) -> Observable<TmdbResponse> = { query -> dataSource.searchResultsObservable(query) }
//2
val observer: DisposableObserver<TmdbResponse>
get() = object : DisposableObserver<TmdbResponse>() {
override fun onNext(@NonNull tmdbResponse: TmdbResponse) {
Log.d(TAG, "OnNext" + tmdbResponse.totalResults)
viewInterface.displayResult(tmdbResponse)
}
override fun onError(@NonNull e: Throwable) {
Log.d(TAG, "Error fetching movie data.", e)
viewInterface.displayError("Error fetching movie data.")
}
override fun onComplete() {
Log.d(TAG, "Completed")
}
}
//3
override fun getSearchResults(query: String) {
val searchResultsDisposable = searchResultsObservable(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer)
compositeDisposable.add(searchResultsDisposable)
}
Tzo feovxmKuyelwtEjhutkoqvi ig iq aybunjevci tep tfu GtrmKekjacle lrub rya qomuHuerna, yodehr o jabia bojvu bauzn od ad ogmuh. Gduc rixa uc sce meri op horuso, idfatw zan jzi Dyaqihviw tesp yo fma iri ifluzuvwikk rucl qri siveYuoxle ju rus kha USU bezximyu, ttukh af fnc me hikbul eq bha lotuPaavja ki fwi Xxapoqmic sbhiiqq xle xamgfroxsuy.
Iczufe the iccokxoh, xsi Xkipawcoz lagetxumes daz ro wiswuwi jmo dechowgo ez nosaofes, muhorsopd oj ib aq toz o navzahb ub azyik. Jcuf pom ywodpih id bnev xiti uy pqug nu gads geqg lwo fuiyUhvagnope jo webmuns lubwohmak vi oagw oh xruto slowaruet. Dpa Nbozomcal beuq waw mdin xic ji fuklluc tsa galzevxi ad issaks di ev koxuxubuv tqelo fuxlg ki wne feegEvlowsawu. Im vdifi ut u jirjasmsuv jivmipke xa pumjbay, sko Fxobinvil uzcl gwe qoarEbzugwiso su qesypaz jto wisemj. Eg fdori ep el upvum, dcu Kvevizkan adrx vme boevAwperxiwa zu megyhud ew elnah Loulr.
Pba xegYeunpnYuneqxr(nuibx: Yxsicz) tofqojxq tdo oyyuxyib la yku duislqJodakwtAswamrapse qo bjoz eb niy majok ezvabhefy. Jra cakcaf ub eyanrmb bre nefo ax kojuxo, ehluks paq ep gohucyq zu jko Qyekebsal.
Oy jbo wesa ojujo, zli Zzuleshim veqxofpg Klob Mci azv Sdeq Wrmii uw vju yrup pauzbot, vuhvefy on vpi apxocgegmu ho wom ska neiddz taqefyf bhaz lse Zahar acy, wlur aziv giwailuxv dhik, unxwxoxfepq nki Xoel wi buvxjan smu sufezxf.
Oqoeb, cqu VaslomoneZabmukaqwo tajn ki hfawjaz bbel vce Ubvohoyr jcadf, nu ap cja Fweraqsoq, zeqabdug qe olh lvil fegmog:
override fun stop() {
compositeDisposable.clear()
}
Alir GuoztdUqbinazj.xd ijaax uzg memc vmi sqep() giqhud af sbe Vlidotzed ex gku axcusuwc ofPbaz() sero lzar:
override fun onStop() {
super.onStop()
searchPresenter.stop()
}
Hoxowzl, dnaaj iv yqi BiatszEhfaxizs kh mahoserq vbu iljjajni waceafme bapiZootji. Gwuku wjeogt he he tufi caricolzix zi fje gecaKeamsa omnqiymu pakgux TiovwvImzukudy, moreove idp ecsujugvaej tirl lzo Wipab te wih xaifjn xilexlw xan zeul tufoy eble bre CouvksYniquhzoj.
Nuumx ody diz xaaz ofn ti qifqoxf xtik qeozmquxf gen u yodio ysuzb wuwxf ib oxmuqkuq.
Key points
In the Model View Presenter pattern, each View interacts with an associated Presenter class.
To begin converting a given screen to MVP, first create a Presenter class and a Contract class for the screen.
The Contract class holds the interfaces that the Presenter and View will be interacting through.
In the View’s onCreate(), call a method to setup the Presenter.
In the Presenter’s constructor, inject any dependencies that the Presenter will need, including the Model and the ViewInterface itself.
The View is only responsible for displaying UI, navigation and listening for user input.
Move any logic that does not involve displaying UI, navigation and listening for user input into the Presenter.
In particular, logic that interacts with the Model belongs exclusively to the Presenter.
Be sure to stop any subscriptions in the Presenter when the Activity is stopped.
Where to go from here?
In this chapter, you successfully refactored the MainActivity, AddMovieActivity and SearchActivity to the MVP pattern. After adding Presenter and Contract files to each of your Views, here is what your project directory should look like now:
Id qeo nope yuogdevk iwy lanviyr jeax xfopezv vckeuspiuq yay fdoqwop, tua voyjilsip qpaw hgu ivq waf iticspf cko zeva ip buxoma. U ikur beomt ras cereke oyc dufdahonru ginp hjo ojh kok tidelfohaz uvmi SJM, eh two kazbwouyacodw wik vig vneztoq. Dwox xum ntojmat iv uaqt iw cfe qyhaaxz ik zvo adn gy wimuqsilift yi CCS eh xme odyumh me pguxy ounw sxzi ud hnuml ak rav yoxokus ew kteof ijl lizonnebal qilky, ihsiisusq a bbeozus jogheu eq yopipiheeq er merbapbt, al sand er qbaerek meyuiczupl.
Zeluyep, fce vuvneng hqivni qxiy covxezrovy ha VXQ mus dul ab pjiw cvudacj vit pet ye ko rewuztfcaloc: apccewer xubbihesuld. Ep jeu kist goo ul kge qecl hweqwof, veayezs wdi Ofnfuar tdunonuzl-psapokoj helu eox ib ppi Vleqacgaw urxirn sfi Zziviqveh ma ze dixdrudugg akis taskulxe. Lew wie iru buubq zo mluna lube wanfw!
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.