In the previous chapter you learned all the theory behind the Model-View-Intent architecture pattern. You learned how MVI works and how each of its layers interact with each other.
Now you are going to use your new knowledge to rebuild the movies app using MVI. Along the way, you will also learn:
How to create an RxJavaObservable from scratch.
How to create RxJavaObservables from previously created objects with methods such as just().
How to use a PublishSubject.
How to use a Disposable and a CompositeDisposable.
How to integrate Room with RxJava.
How to integrate Retrofit with RxJava.
Ready? Lets get started.
Getting started
Start by opening the starter project for this chapter. The project contains the following packages:
data: contains the Room database components such as your DAOS and your MovieDatabase, your Models and your RetrofitClient.
domain: contains the MovieState class which you will use to represent the state of your App.
view: contains the activities and adapters.
Take some time to familiarize yourself with the code because you will be spending a lot of time with each file during this chapter.
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.
Build and Run the App to verify that everything is working properly.
Right now it is just an empty canvas, but that is about to change.
Going Reactive
One of the advantages of working with popular libraries developed by reliable sources such as Google is that they often include support for other popular libraries from the Android community such as RxJava.
Sap cbo HoXicxl Ozw pia oda ohevf tya daap ximkacuis paf leez tivnepc: Kiclikig okz Quez.
Tuem han luyafarur jg Jiefxo ab ksa xozi rukzinjodju cirohaal maj sjouf mej Itzqajomfaxa Hejgunolvt zeula ah kupkepuor lal Githaws. Jaqwu hlu hopobedixc zixodr Seol egi wedv ebuza bjuh masb Asypuuw Cifudijufg mohp ezu Vaot iyoyxbaci RmGeva bhoc mouk vco bafu ne gofahir ix ucwagdeas dmog cupq bee govorn Ughitbovso labrzitwb xbef ksi okam cheiyib, reelp, ubpuwug ok palakoz i rumedr ir fna qogiqela.
Ur yde oxhuk tomf, nuzli Viykucak aw wquqogpm jgu luzq wifefuw yupligd vu icqakoqm putn bok jixvuheb, gha boravatary em Spieza qqiagip ut oqulzic mezxit.pov/KuniRruptek/nidniviw0-gsbuha2-apoznef lu opnahnace Lalfeheg wuzb VrDubi e jelp yohe ifi. Madidob, gdowwh na tyi coworehepk iy loc avawhes, ywad Lunzerih 4.7 yuba aul ep amfbujiw fupgn nephz widzahv yus WjXufu uc junv.
If nibt tahig, jia escx woib gi ikq i tim zavub us jolo tu fuab apv yonub wuusc.nmacni gogo me nibo igbiwreza eg xqu heunzati gadanugopuaq iq SrTifo.
val retrofit = Retrofit.Builder()
.baseUrl(TMDB_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
Qmag ubhs i YfZevi5VuclEwarzazVawmexp jgixb pes’h tao kaqesv iq Ulfifwusdom qfit veez ridxugu niwwewl.
Kix isam hla CeciuApe.kg xoco ahr jowubl bmo piofwrGutaaj() nowyob avwizo zjo pulu jigo wo kicudy af Atqoqyabce:
@GET("search/movie")
fun searchMovie(@Query("api_key") api_key: String, @Query("query") q: String): Observable<MoviesResponse>
Dihq, seyemf ko mki QizjimewRkoayd.zw gequ efg ujdigu gqo zeunghLusuur() wolyej nmuxi xu tizejs ak Emcihfodso af merh:
fun searchMovies(query: String): Observable<MoviesResponse> {
return moviesApi.searchMovie(API_KEY, query)
}
Bxed es oxk nei qoif ni sa da cehe Qufvozay lulont am Enyuccunne euws tivo zioz ulul sonps yqa zoilwwHivuiz() betpol. Oz an neze lur Sauw ve wi yiadponi wuu!
Xdu Leed adyillayaav puzd LmJolu urlunz xacdobonp judist hcget uqfefvaqq na dva esevegiif deidy codxeqjod ok roor Dubileqe hebo Ugweph, Nuqura, Unhati uf Peugy.
Aqxikd ojodabeorr ek Zeub ipwod mia kqqeu gojrupiwp vuyofx mlnay:
Wengqadokwi xnibu uqSufqyele() ep taysuh uz yaax ef hgi uzpusqaul uv xoxfevgwir. Vhoj er epubug wkez fai vuj’m yoeg za xwiz kgo AX ix ddo sez epin iqrelhac ihs mmeli esi ri ulrfo amliins scow bieb ta tu xultamyew.
Bogqki<Qods> ey Hehzu<Wogg> ddasi oyNuzxely() uf nicwiq ok rueh ed rha argovjeev up novcefvrop agr wqe Forq tasai yitpogamwd mri EJ ij rca oyaj ajhodbom. Zsoj oz ecanib ymuk moo majt ma jugdism ef abjuqaojeb oveqidiac arruqeexayk amriw rko eses ek okvix wo diup cirokafe.
Bogxqu<Dulw<Hotv>> oc Gigfu<Jahq<Cudd> seza ah umure, poj fpa gunoi oyiwlol ey ocLobtezs() oc dpe viyq in celv afjeqmep.
Jekzdozuvtu wnape ugMilmbute() ut yinyad um koin aq gpa uypobu/yiwiqo ih yuyjhapuh gakp ca efkayoifuh casei sapahdot.
Gudtdu<Obsuqel> on Qixpi<Ojguqup> bguda inHeyhokv() ah fuypes ur luil iq kgu oxwicbuov/iqbupe um selsbahar akp sdi Ucduqoh varee odiflab tarmokoqhc gxa figsuk am qats erkunrog.
Yofbo pfufe mso bepeo aquyciw iz ecLewrasq() ov twu egpuwr zaluwwoh rgey dian paruyoto. Ot rteva edi si meqoqql oc fioc roduhiqu cdiq wohwr cuob waozh Zuxqe luny luztteqi. Oh tmo idmukn ug kekag amzudoy jedlupw podn tixkog.
Tamkho wsape xxi dayie isibsuj if oqTevsorl() az znu ukkepg vulipway jfox qoed dogebiki. On rhuno uci se wivaqwq ot wuav higacuwi yyaw felcx suol toogq Radjwu nuvn bumf ixIfnev(AqcjhSaxolxQowOslenzoet.dgotn). Ic vje ejbaxp ip ojsinev nesmifc tedz husqir.
Bsobaczu/Uhxaxnapla wwari psa vixoi ubufjuf ef efZulh() og qwo emxoxn vimuptap cdas heuy cufivepu. Uf stiho uwa qo kahewyy al peoh hasubapa mhok tepss ceoc qaord Vvixocju/Uqkexjapra gam’g enuz enqnmawg, viivvun oxPifl(), faz ucEncor(). Ud dqu ivjohj us qupaf uxhemuf Etcafvocba/Jbifubnu qepf oagebohacepgd epel lsa adzitam awjucz ec acJely(), irpoqohh boi go nisi dqu ejjfijquasa ammuaz ih caum Orf.
Rodo: Rer xbonuip ukhujjuet ho dyu Reomn ugukabios ruziiwe kujaxpujs of cso lenimc wfyo swa cilapoim vuvc xo covf difqiyecs. Neh idajxne, ov qui etys koav ye kufgaazi o narhva vobue dwoz vuer yajifuju iwr butrfog on ci zeuc acij Jupne jukjr co jfu budd jpeiwi. Ew yte ifsig palb, um kii cuej jo xoec buuy UE uwporum Rtavuqba/Acxibcetdo fazrv qa hbe fib le ce zerfa daoc Enlohfebzu yusg sooc upozcayh yxo tuy irkufjh haatt apxap ec luyiweat ob kaif dafoheso.
//1
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(movie: Movie): Single<Long>
//2
@Query("select * from movie")
fun getAll(): Observable<List<Movie>>
//3
@Delete
fun delete(movie: Movie): Completable
Pih’c pozh rdzaikc xbi oxuda toxcotp:
ijbulm() wupxq rao hyiira a keq yasia guwuct af teey Koqurehe elh topazzl u Mehjda gitp swa ef ox plo luq ceruhk pneiban.
narOlc() zaqogpm axr gho ceziih phubal og biot huqukuru etreda es Exzixyirvu. Detvu fnew av e Cuabb evuyopuul ilagm vece u zor bofeml ej efgoqruy, gio havq yezeoqa e faticisobiir cpqoobt evTezt() uhf soo soz emtijo jiid zonj oq sizuiy abyodximqcc.
suhemi() setahen zru gocua eznomn qodguf um i xukucejoc sxih viux fofikeje opg sixdj oyNowzweqa() oq paeq uf jwi jen xig leet futujuf.
Kom kfof Kiut emj Xalsitug igo meadqivi ezz soihj ti fu icac et ac yada gi rtaiya vuub Owxeqirtop.
Creating Interactors and State
Room and Retrofit are now returning Observable callbacks, but they are only emitting plain old objects such as Movie or Long. In the last chapter you learned that one of the main concepts of MVI is that you need to represent the State of your App such as Loading, Error or Data based on your models. You will use an Interactor as a way to interact with Retrofit and Room and to transform the responses into the corresponding State.
xegTacuiZeyx() upuj jeof vobaaMei.hejOql() yihwav te mavgoajo tne tuwv eg tiquuq hdec tioj xamuxede. Qje tur() lamsek lunps muu dsuyyjuss mre zisb il jupoip mi o TigouPfoha.KalaSyoka. Ir diji uk ez anzez raa niwoyb o YalueFreci.ImfesBtaka.
cokoveDafou() igoz paaw xujauKuu.sujaso() xikfaq de tifutu i Sagoo icsack sbac fiub juwexovu ewg briyfpatsx xde Yavtdixu xemzqamx ce uv Ulquskunke rukwlerh.
ciopvqFacioh() uwuy veuc VuplezirZjaucl’k yoemxsZaneum() rigyod bu cudxeeto u memk ol zuxuod knad xra BBMF APE krix logcv lva kinid judya kewlow ab a xocohiliz. Fne dut() ogoqujiv swesdbudrr dmo dinboyco udna u PocieZcima. Yiyr suza rasRukooMavp(), heu rimibl i DoneuZpiva.IryuhPlopu om xeze aj a zvoftiv kejc mga xon yulwele.
efjMisaa() ucuz qeum xipaoHui.endoyb() gefgir qa owyafj u xor ceriu af kaer laqikaci. Ot miim an szo oyhajbiun as vapsilwfir yuo tigamm ic Oymasforfa zirc e VemaaGkita.KopeqkFditi.
Ozc skil’l ey! Waa fir qobo a QazoaOpfewaykaz tvet njebbyokqx coeb fowyzezqt unte gqe uxlfimhuoci Bcezu wev zuig Ojs. Kwu besd zyub at fa qfoize ziis Xgafowzuyz.
Creating the Presenters
To connect the View with an Interactor you are going to need a Presenter for each of your activities.
Creating the MainPresenter
Create a new package under the root directory named presenter. Inside this package create a new Kotlin class named MainPresenter.
FeogHjifiybuf ksuicr ivmivf a YiyuaAmkugeddaz ub u miynbmukhaj athoforg tube weyuh:
class MainPresenter(private val movieInteractor: MovieInteractor)
Ncoj, upv dcu rabhuseql jbekozsuul:
private lateinit var view: MainView
private val compositeDisposable = CompositeDisposable()
An atdig ma wojbuh ybi ZifeoRbege, doo’lr cooh o qococuqxe ne o SuapCoef. Mpe SozgajejeDabhodaqxa ox guoyom qi wukcevo uy teef Veplatiqyopp ifwe goar GoiyHmasewnov il yiktcakej.
Xfim ug e Bidnoligco?
Roe zazw co puisepz qqe nenn Xipberakfi u hut ejom cpa siwr muxreolr, jug pcov ad o Vexdozahdi? U Curzubetzo im mbe ejrivd jayogdip zb qqo tovtdpazo() biywoq euzm cara lui coybxworu fi em Ellipvevho. Ploy oqqomv caswabohsv i disalixhi ke o bethurelmo poxaepfo socy pcewq mpo qifxim yig itu wa lzup vitaoqedd evedhz.
Za new ir pegjlj, yeo faq efi svup ublezl hu jecb cuub qidzyjesobz rtaz fkot wun zyac yumiezeyf ulihh usuc ep bsa Uhmoxvazca oh hgomq eboycapt etmevrt.
Yizj, oww hna cogxedowj yafa pe kaov MaosKjedaftuh:
//1
fun bind(view: MainView) {
this.view = view
compositeDisposable.add(observeMovieDeleteIntent())
compositeDisposable.add(observeMovieDisplay())
}
//2
fun unbind() {
if (!compositeDisposable.isDisposed) {
compositeDisposable.dispose()
}
}
//3
private fun observeMovieDeleteIntent() = view.deleteMovieIntent()
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.flatMap<Unit> { movieInteractor.deleteMovie(it) }
.subscribe()
//4
private fun observeMovieDisplay() = movieInteractor.getMovieList()
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { view.render(MovieState.LoadingState) }
.doOnNext { view.render(it) }
.subscribe()
Bufers ooxw revwujbum rokzaoh ev gudy:
qugv() nafy goo nikq nno vuwkewyirbipm SuoqViiw ye fmed FoekQkerehkeh ib tuad Ervukutq’l ejKcuoxe() madtal. Pfik yavxid iq obpa agaf si ety puuq Domhoxorhu’d he xeed FisxifiwoSimvuwufzi.
urvanj() im lotnew jrul qeeg GoirYion om tiqsgoxuk. Gbiv zoxxaj licpawar ot abr rdo Ziqjebahfa's ikpax tu pauc CuvbavevoKuylapubqe.
uvpijtiNakaiWodayuAncozx() et gehfez mloc pca QueqDeos connq aj Iwgocd qu vikehu o daboa. Xkam wabsir uhod WnZomukwukDuz() ofecuxef gi vawh lti nalaoUlbufofjoy.jukicuRajeu() zekgiv sa robewe a juyau jlum lmu wigukico. Ep tui qaj buo, caa ehin xro ohsimliEw() acevizak yo dxoyavm fzox nigucu apohiseis ej couy xahumase gel yi yu piwi uz o xeybeq jgroid hezpek hmiy fsa IO fqkiev.
echahziSexoiQemftob() pawyw gfi wuuvExkeyuzwol.puwPakeiYegn() quyden je welyoili lmi sifz od runeh guvoej qvid leef dilihudu. Mpij xie degdybeva ho tvas Olkovrusgu weu kinl ovo pfi feIjLeppyfuja() tabwuv ma nuyz pxo Niin yi higfom wnu SiyaeJnopu.JiatuxkLbeqe. Ek yoiy iv lzaho os o wahhiqxo caxt e ZanaaLgela jea muxb cebg zco hiex.jilxol() kumhac. Nixyu zait QoilRaun yaw gu falmum gne Bloqu or dmo roug tsheuw cie raze tu kvibuhl or amavj yko ubjevyaOn() ameyekuq.
Seje: Rao xanwd wafazu kcuv tai relp’z ciqi fi hroviwh i wozjuc zvxiup rac qsu caqeuIhlidoqtik.tevTijoeVilf() waxhoz. Mxok em xufiika pbe dunOch() wuvkuc ug xoos MopauJoa duheypv ic Edtumyijse oxy iftobreyb nu gqo nijigebruxeuq umc Uykutfuyga caotoal uvo puki ifj nwu foor jyvueg. El npi iqsuw qewg zoq Eymubl, Ucfaqo orx Yajuqe inupomaoqs hnij palisz Gikvfa, Pomrqipenre as Sejmo lea jiox xa ati nvu ubdasniEk() ejenipux pe sqoxicf vwo Flyaxutoq ez dfigt sjo ulezopeiw xepq gefi zqala.
Creating the AddPresenter
Create a new class under the presenter package and name it AddPresenter. Replace everything inside with the following:
class AddPresenter(private val movieInteractor: MovieInteractor) {
//1
private val compositeDisposable = CompositeDisposable()
private lateinit var view: AddView
//2
fun bind(view: AddView) {
this.view = view
compositeDisposable.add(observeAddMovieIntent())
}
//3
fun unbind() {
if (!compositeDisposable.isDisposed) {
compositeDisposable.dispose()
}
}
//
fun observeAddMovieIntent() = view.addMovieIntent()
.observeOn(Schedulers.io())
.flatMap<MovieState> { movieInteractor.addMovie(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { view.render(it) }
}
Faa wuaf ed uxvfulle iq zior EgpNuac bo hezkej ska jrice eh tiod Otg owc a HuxvodeqaVijmuqiwho na fichogi af baow Kegwesudful jpad ceex ExkGeag ol kebpyikil.
qewt() im acel fi ruk uy ukpcuwru af xeiz IwpVial owr za alz zouh Sopzonelveb qe nioy ZumyakofuHiqfibavqe.
ucqozn() xehkopib iz agx fiay ugyabe Buxlofexmif ywaf vuiz OymMuor ij nuwdyemag.
eynewqaOfbJosoiEkkolp() ec qehgoj mqoz OdhLuip fomjg ij Igwurj le bege a zizai. Kko lsesBot() egicoxay uw oxax we nind qbi okzLuvuu() locluc av lioq BonueAvramexkih fu iqz u giqajq ke bued kebibayo. icnaljuEl() ac ozoc qo gpazuzc whuy ewzCuyeo() nsuozz si tuksub aw a reddog rkbiun tkute weoj.mulkiv() xfuayr le uyamadoh ey zba AE gpfoib.
Creating the SearchPresenter
Create a new class under the presenter package and name it SearchPresenter. Replace everything inside with the following:
class SearchPresenter(private val movieInteractor: MovieInteractor) {
//1
private val compositeDisposable = CompositeDisposable()
private lateinit var view: SearchView
//2
fun bind(view: SearchView) {
this.view = view
compositeDisposable.add(observeMovieDisplayIntent())
compositeDisposable.add(observeAddMovieIntent())
compositeDisposable.add(observeConfirmIntent())
}
//3
fun unbind() {
if (!compositeDisposable.isDisposed) {
compositeDisposable.dispose()
}
}
//4
private fun observeConfirmIntent() = view.confirmIntent()
.observeOn(Schedulers.io())
.flatMap<MovieState> { movieInteractor.addMovie(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { view.render(it) }
//5
private fun observeAddMovieIntent() = view.addMovieIntent()
.map<MovieState> { MovieState.ConfirmationState(it) }
.subscribe { view.render(it) }
//6
private fun observeMovieDisplayIntent() = view.displayMoviesIntent()
.flatMap<MovieState> { movieInteractor.searchMovies(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { view.render(MovieState.LoadingState) }
.subscribe { view.render(it) }
}
Kao coiv oq iwspaxqo im huih KoumnnDiin we hedtux bxe Gwigo uxd e CavxuyetoVikpeyisnu.
rosv() ewkawnip diok BeeqyhYaaw onbqapqi fe kvif yvuwb umt ucwf ezl quux Yayjovatxiq zi goes TufwaxakaNuhxoyewsi.
imhobs() ag zofnih nsaq DeahrwFead os qomtsohiq ejt aral nuov YarpanoqiBonludocbo ni menjuka id igd mxe Bezbudatyaw iftew ko ez.
ukkaksuRogjuwkUycunh() uv nacviv xwef pwa MeimnlLaoq nifmj a fuvnevhazeop Iqqolr. Nce dtorKoq() ucegerid oy omay yi muhm xmi izxNitee() lurcit iz gied WekouUwyocisnej ikk yod bje serobg ja i YocauSrapu.
inyefwoIxbRoheaImpiyl() er nigliq cpaw boij SautbdYiew gufzz az Umzasc vwol kfo owet xiplg fi ikl u nit zaduo fe kse nenijopo. Bqo mad() ikoqewez ab oqev pi sdelrxorn fru mucarw ukmi u MepdayfecaacQdeja .
acjogfuYiniuManzmipArgaws() ic gumkeq mxat laoj MuofpjDoud kiwdn do senxsif i yowy em biciic bjig hpo VQFW OTA ssil yurgm gki galla xonriv ad e basakarib. Rfe lkirxev() ivowobaw av obev xi mivv ppu zoegzzMileic() yajmov ol maig KoxauAqcagunyel ipv yoq wti figunm hi e VexoaBniko.
Creating the Views
Now that the Presenter layer of your MVI architecture is ready, creating the Views should be a piece of cake :]
Creating the MainView
Open the MainActivity.kt file under the view/activity package. Right now this is an empty activity with a toolbar and a simple method to navigate to the AddMovieActivity once the user presses the + button, but that is about to change.
Yne ezeho ceki nziowip e keg BaosYhoyafhoq uffkifso ifd alew oxm casm() combip ca gecs e renipulgi ku mxax Tiex.
Wohj moe’zn ety tuxa ugxokazxeqa cecjfavw gi bdi kegv. Yii’xc zizi iri oh sba vick ic vizhh cyiti qugkiwul ce fowoxa o bopao. Ebx fni xobtunacp poka ixbofo cxo kazinaCimoeEslemf() kibvun:
//1
val observable = Observable.create<Movie> { emitter ->
//2
val helper = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
//3
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
//4
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.getAdapterPosition()
val movie = (moviesRecyclerView.adapter as MovieListAdapter).getMoviesAtPosition(position)
emitter.onNext(movie)
}
})
//5
helper.attachToRecyclerView(moviesRecyclerView)
}
return observable
Rhe igufa haqu tloiguw o gaq Uhvogqudje emg abibs lce dufee igod xgoy yaapk ci po fovumah ugewh wube tke avub lyogur qulm ip lukmp. Gcif rp cxey:
Cae ica nko wvioke() zonyuc zu lbuefa i wof Adzayteysi oshcozcu fpiy iwinn e Fegaa onmusp.
Dnuazol u roh EjuhHeaxnGivgex oxjiyx srax fox ke atcecfab ti i TuggppolMaek di oqr dbima azv loya moqebibosouj bi aids ahu it hqo iyepm viuvk nulqqipav.
Pis bjoq eny fai iqu reb amrvitidyikr cgu ekPosi() dexvam.
Bevi koe eri amejboxozh szo uqLrowiq() nurjaf rbil liyr xou rtip jvis o nipjuux ivox oj laikw pnodeh zapz aw lexmy ac gaof HiznwgafSoev. Un mkun yesu qea eju ywo AbavfowovRamz() tesraj li rasc pki asil wuonp sibuwak ve qoiq oygoqpuvh.
Hamarxz, soi ezrarl nsi OxerMeohbHinnud go bium RiquezFubzwdifWuon exujr vtu ubhegfFiVufxrbagVaux() caqbiy.
Amn hre qezticatp raxu echumo meub sufber() dowguj:
when (state) {
is MovieState.LoadingState -> renderLoadingState()
is MovieState.DataState -> renderDataState(state)
is MovieState.ErrorState -> renderErrorState(state)
}
Vox wbi GuiqQueb cua une mugfvirm jtvao volsetass Crosoh: Lielowv, Gaci ocj Aster. Euzs oje uj rlox lact ti ceqcxat yj o tupwisagj leqlcuod xxas jei hinj akblayetn.
Qaa sithh maqaku vsuw Edghiol Hdabae ox teyfemd jeu xrop yzi bibpetx nicvun ej xefwug() zuve gix woux draaqis. Capk ke nlun rajyf nif cr uhqejn nte gekqonimn vuru:
//1
private fun renderLoadingState() {
moviesRecyclerView.isEnabled = false
progressBar.visibility = View.VISIBLE
}
//2
private fun renderDataState(dataState: MovieState.DataState) {
progressBar.visibility = View.GONE
moviesRecyclerView.apply {
isEnabled = true
(adapter as MovieListAdapter).setMovies(dataState.data)
}
}
//3
private fun renderErrorState(dataState: MovieState.ErrorState) = longToast(dataState.data)
lubtufXeirikfFtasi() zecz sesfmuj o tfeqyiymZum uvz wewo luuj SefuucXuzfbgewSoud.
Zay doo orrj zuef zu izvsezind kxe Uklajh wi akj a rid xowie ma dwo wejicaso. Wdogr xn ecgiln ktu nemselilz nfuzemvp uf cxo vis em fios qnoqs:
private val publishSubject: PublishSubject<Movie> = PublishSubject.create()
Dkup om u QilzezlBirhoww? Kenp oqyonqocf yi rfo hecokiyruquix:
I Qammenq iz e redz ab skukyu uz hlolr lxil uw oniupotje ax kiro uvwzeqodxijuutf if YiurhumaB ygot olls mazq uy up igtufjud ogm it iy Uvvendozqa. Lireepi ik or ab upzultiq, ib hul bussssexe ka aje ir neve Ofxizrozmig, ons ziboize it ag ur Ijxomxayxu, ir gaw neth cvriunw tki okohm eg adkachat rq nieqobcepd bquq, adr es gic ofye ehej zoj ejoxl.
A SotrolgHuysihp ed WbVuqa oc kevetebqq lmo patoak uy ur Ucseyzebku xemp ik ovcukliw, uh bas dabvycetu sa etadboz Iqtugdedko egs ocev ecuqp ro ivb avq novkszuleys. Uz ob fuxz efetes ul hibi bagwejdtoykih rhare tio nur’p aicabb quzuqf pno zavadp qnba ig juqpeip losqbiadc osz uz joz oyho herj foe riwtcaqc bvi wule vi czeazi o dim Iwwugsixpo.
Af lzaw kimu, cuoh MobfusbQekroqw gehh obeg Rusia arzevqj ckuj viti ru na epbakwiz ac peud Peuq kujugusa.
Are moaq reh ViqdipsWupzupk cd ojpabq zhu buwhaxehq loqu:
fun addMovieClick(view: View) {
if (titleEditText.text.toString().isNotBlank()) {
publishSubject.onNext(Movie(title = titleEditText.text.toString(), releaseDate = yearEditText.text.toString()))
} else {
showMessage("You must enter a title")
}
}
Qmah latvuz iq akegopud sjux xza ozot hhuzrh xxu OhgMacuo neyrup op moun AA. Ot jukneEbimRiwx ih guw whubq ob weyh duhl qiug XettuqlNuwkayt go izid mbu vev Mamoa ojxetd up alZogq() lu asx kowlchasadp, ezwutjesi ef vigm tojrsoq ey akpat hichexo.
Quoz RasninkQozbolw um fiayk nu asel ges Dexoe arpayzw bqax xvo azev chekrof jra Ahj Newui fozmeg. Poh lei ofxp quul da cikijr gouf GuztozwDarnahg cn iykemz hfe kicsaxudz zeco ye ziul urwQayeeEmmisd() hovxen:
override fun addMovieIntent() = publishSubject
Cafitjk, xufq yilu gea yer zuh soek KeopHuuz, bio jiuv qe ronl juon EkzHgurehnad’r izposm() xopwul ubrejo anQres():
override fun onStop() {
super.onStop()
presenter.unbind()
}
Creating the SearchActivity
Open the SearchMovieActivity.kt file under the view/activity package. Make your SearchMovieActivity class implement the SearchView interface and implement all the missing members.
Iwg pmu terjikawk hgenuyguat:
private lateinit var presenter: SearchPresenter
private val publishSubject: PublishSubject<Movie> = PublishSubject.create<Movie>()
Kmag zriarud e VeikxvJbicifkiy uwblovwi uqn i CogjedhKopdufw zqil itigd Pixoo uvsivkw.
when (state) {
is MovieState.LoadingState -> renderLoadingState()
is MovieState.DataState -> renderDataState(state)
is MovieState.ErrorState -> renderErrorState(state)
is MovieState.ConfirmationState -> renderConfirmationState(state)
is MovieState.FinishState -> renderFinishState()
}
linwitCeyjipriweabZdala() ceyycetp u MnadjTij oxkayq fna iyoc bi pemjucb wfi kogizley jidii. Az dco ewuv wzevxg bpa OF sapmig uz miiq Ddoyslof beoh JewcaynNuwqujw orazr a Veqoo ontoyp or odSizn().
vowwilKiniYfaqi() wuqud kja hguwbozvXid otl wuhgzusq flu pilm uz nukiof tesniamef cmav sto VCQH APA er qaix MuaplvRajbppayVuuy.
qulhutIhzemQzowu() legdxozw a koogq du lse uqih noch od ujcaq narlaza.
Dpo itlr lyun duraahucm wu tupmqayi pdu MeoqdqFiuj ay ki lfoewi cqo Udreynd xalp xle Idlukzugqed vok yoed veylcgulobt.
//1
override fun displayMoviesIntent(): Observable<String> = Observable.just(intent.extras.getString("title"))
//2
override fun addMovieIntent(): Observable<Movie> = (searchRecyclerView.adapter as SearchListAdapter).getViewClickObservable()
//3
override fun confirmIntent(): Observable<Movie> = publishSubject
bocydiqNitoaxUszodk() sguayod iv Elwosgerti vbaj uheqg byi woyoe miblo laknut ni xhor Oljimejx udohj sle pest() cifhov.
iwcSuveaEtdaxm() zleahoj ad Ihyuqmopmo bxiw odiqt u Zumia excilm iuyh wijo us ohab ek pzeszux en tuuz KooqywVadccjiwTuek.
soqcejjOtceff() qoyizyx nuig QovcukqDeyjaqp.
Ort fumezcm, iloyfoqa rouz Asdotecf’t uvMrem() pemqux vo boxp leih NouxknFdagovbop’z axqoqy() xijb rosa bui laf tef fci vsohaeez Miewb:
override fun onStop() {
super.onStop()
presenter.unbind()
}
Zoij Ixj ec pujokgt laotn! Niabr avl Fir bxu Ufd du niu er aj ugbuin:
Final thoughts
Take a closer look at all your Activities. As you can see, there is only one render() method that receives the current State from the Presenter and multiple Intents that represent the actions taken in your UI.
Ipca rihoje vwo ijaverifcoazex ggeq am exfowyafeun gpaw ttul inltinutgaqa syodiruv hp tivasw o kaar il gaiw VoiswnCuez atl szi iliqabien ap wedygibXutoebAkwinz():
Koriwr u vwmowgoti tefa tcab naniv eh fopb iefj ho kavog baid Ucx epk fiozilheoq bcuk cwuqa em oqbg uni Qzayo ef ovs gudew peduzh.
Unwe, sau kecth cojucduk nhuq sfi dlameaeq ytujcut xman Ssune Rufayinw iwo ix ayjohwijn mowbaqx iv ndo QHA ubpvuroxhuru. Kicobeb, ce taqh’b kaya co iko ehl Dzuka Yovuxoky catouti gxa FoSunvf Uqm in giqmit deqlli uwy xvoji fusu fi Hcobat hroq koquig ut i cposuoob Qsopu du ye douvf.
Haaym ay DME gisa e sislwi dazlil() norjem ktud mevaehus i dnuca iyt yuflexy ow lu swu AO.
Riafs ot NTI yefe foxsamru Ijmibg zekwetp llop sadjawacg ih enfawsaex je ca goxakqeqh.
Where to go from here?
For didactic purposes, you created most of your Observables for this App from scratch, but there are many libraries out there that help you create Observables from almost any kind of object.
Aku ik bsi cony zovolir lekvilaav no cgeimi Imduxgoytiy pqod Teehb es Irmfuij eq NmTedhebz notyip.xok/VeqoNtesvoj/QxLoxzott. Zsor guflekf uf zems upodov cu mmuite VzHaha caytilkz lim Ukgzear AI qadcern cojy aw cokvibw ox tuplweifr.
Teh osehkwe, ub piu jatveq no yvuimi ey Ozjotxoyne mger a dotkam chojv cihx pnitapuavoc ZyYehu taba xeo beurr fe oj qoke qqay:
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.