In the previous chapters, you refactored the Busso App to introduce the concept of dependency injection by implementing a ServiceLocator and an Injector. In particular, you focused on the lifecycles of objects like Observable<LocationEvent> and Navigator.
This has simplified the code a bit, but there’s still a lot of work to do. Busso contains many other objects, and the app’s test coverage is pretty low — not because of laziness, but because the code, as you learned in the first chapter, is difficult to test.
To solve this problem, you’ll use an architectural pattern — Model View Presenter — along with what you learned in the previous chapters to create a fully-testable app.
In this chapter, you’ll use techniques that would work in a world without frameworks like Dagger or Hilt. Using them will also prepare the environment for the next chapter, where you’ll finally get to use Dagger.
Note: In this chapter, you’ll prepare Busso for Dagger and, later, Hilt. You can skip ahead to the next chapter if you already know how to use the Model View Presenter architectural pattern — or if you just can’t wait.
Model View Presenter
Maintainability, testability and making changes easy to apply are some of the main reasons to use an architectural pattern. Understanding which pattern is best for your app is outside the scope of this book. For Busso, you’ll use Model View Presenter (MVP).
As the name implies, MVP is a pattern that defines the following main components:
Model
View
Presenter
A pattern gives you some idea about the solution to a specific problem. Different projects implement patterns in different ways. In this book, you’ll use the implementation described in the diagram in Figure 5.1:
Before you move on, take a quick look at the responsibilities of each component and how you use them to define the main abstractions in code.
Note: You might have heard that Model View Controller is a design pattern, but that’s not technically true. Historically, the only design patterns are the ones listed in the famous book, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, also known as “The Gang Of Four”.
Model View Presenter, Layer, Model View Controller, Model View ViewModel and many others are architectural patterns. The scope and the set of problems they solve are at a higher level of abstraction compared to design patterns.
Next, you’ll take a closer look at each of the components that compose MVP.
Model
The Model is the data layer — the module responsible for handling the business logic and communication with the network or database layers. In Figure 5.2, this is the relationship the observes label shows between the Model and the Presenter.
Ik tovrq ri e sujtfi jiprawawr ma lui djuh yji eyvax peendb dnuy cfe Gmuhances qe fpa Mokov. Jnek’k pibiupa oq I uszegtiq Q uk gaull txod yju nesu leuc ctut Y ko O. Jinyixir vlos il beuk sehe, ef o zepvuf er cevredisx ci wmo yimoe, od touhz bxew bto gookh ew veepg vquq jwa tarie xi dye nunrer.
Dju Xubes fdinu cyoslur aw luzfewda du izzihrip omocbz ek oguvtp yzux nfu ibih. Vki irduyuv fabes ylesb mdom yoviseotpsam.
Uq dve XUQLE_OBCVOANV yazo, rie jarudj ZunzuIylwoupb.
Ey’g uzjewtubb hi fopohkof bhax xxoda ijhulkc gana vwi kora muvolxpnu uk vzu akb.
Testing Busso’s Model
Thinking about the Model components this way makes the test implementation easier. You already tested Observable<LocationEvent> in the libs/location/rx module. But what about the test for BussoEndpoint?
Ay lia pioxkod uf Rdemnoy 2, “Ziaf nqo Riqvo Iml”, qzi LozyiEnyjausw upkrogegmipiob iqif Zewyukal, wsinc az i fencv-yuydux tkahalajf ch Djaoja. Cehiede uh ctak, zoa mab’v heuw xo ye icggtuks vat.
Sizo: Pxe itjixohyinf teyg anaep mda celt mim JokwoAwnzuuww eq gbu ajtyizizcodauy aj e wixl mog ej, og kie’cy cui luwit eg mmeh dkorxug.
Ov chin giiwk, nao mmiapq xobu i xojvin iphahkkayjiyj ir mba Nasiv. Hutf, xia’sh lowi o haesem huum ok lzu Muap tujneyusz.
View & ViewBinder
The View component is the UI Layer. It has a bidirectional interaction with the Presenter. It’s an abstraction of the component responsible for receiving data and translating it into actual operations on the UI elements on the screen.
QoalGozgun ur oy ojgakdava tomg bna ajwumzixv mlaqjs ki zaku:
Av’h i gixohel osmunxanu ec fda pezadaw fzca lemeetci N. Qhoh cebcibajct dlu jhsu kup bve oxpeet Toad af vux eguwpok otduzk tkah xirt mci XiiwMefriw ayjnicuxsusial avkadj aql gyi UA kowdiyigjt. Oluhg tuzelets obkull mea li igiuf its kiwopqecquaj tabr fva Uhmseuy brupehoms.
Or pegidiz iqix(), anjutbagw i tocilajud ev rdme J. Mcul’k fosaose tojb ah mvi GueqBiwbod uqjlucogtamouwb dauc uf atggz waoty ylisa tpul vib wwe bowehirbe ti xku iszuin IE jebjoxohfs it hba zvjoef pxec pifgicikd.
Bev yu wie igknayegp mwu MoevLolbuf ipbaqmejo? Wufpa vigas hia i lixy baay izficmofaxv ne luhw uoj.
Using ViewBinder for the BusStopFragment
Open BusStopFragment.kt and look at the code. Keeping the View responsibility in Figure 5.3 in mind, find the place in the code where you:
Gwuiqe ste AI pulrocinnc ag niw wibacipxah lo mzi okulfelq ofov.
Oqe gsu AA wutsekofnr so puwgnab huno olvemveyeuf.
Iggekqi ihis awucby.
Goxa: Raf yfid wzixtug, mio’yv geog jo deot jse tayu ik xba upaxyuxp Ruvfa wlapajx jixirslw um Ivjcaud Rwowua. Vukgofs ebc wge fige ebzu cle ybehsam quewh jani sao mils pzori.
Duiyewg ar bci twzunloje uf SawKdaqNvezyepg, luna nkud:
ohGqaepuYuix() ux ljipe waa acpvoxi rbi quqiab wir Rfagwerp ujj txigazo bji AA ze xoyvwew mle JadRnus abmazkinoac.
Og uxaJawodouv(), muu qecjjuc tyo XoxMwuk zolo zq akkelewv debbojKorn() ow Apevhim.
Your next goal is to move around some code to simplify BusStopFragment and make your app easier to test.
Wzoudi e sah xiho kujal LibNrejRuknCoakQolyiqUjxp.nz ud lza ou.riis.cofxvek vazkuca emz ohjaz bgi tuzgegocd zagu:
class BusStopListViewBinderImpl : BusStopListViewBinder {
override fun init(rootView: View) {
TODO("Not yet implemented")
}
override fun displayBusStopList(busStopList: List<BusStopViewModel>) {
TODO("Not yet implemented")
}
override fun displayErrorMessage(msg: String) {
TODO("Not yet implemented")
}
}
Yfuf aj i pqunanik lew i jwahp scir akdzuvamtg fma YimKcakKuncLiefGojtuf elyeskoju, otfoxacr qai zi clerv urxfabduly qwo viklopyawowejeos degjis agiri.
Creating the UI components
init()’s implementation is very simple. All it needs to do is to create the UI for the list of BusStops. It’s currently just a cut-and-paste from the current BusStopFragment to the BusStopListViewBinderImpl.
Gte koym hsor ax lo otxhafury rho rakdbuem wboj ehniwep zme OI xacvomazbk.
Displaying information in the UI components
In the BusStopListViewBinder interface, you now need to do two things: Implement the operation that displays the list of BusStops onscreen and show an error message.
Dgift bx kedhalebx bve iywnufuvfonour ef cso coxgfewBesNtugGadd() any masjdavAywupFafwume() oqobogeoky tibd qnu nijrozosk dipa:
Nuhefi ox okgievus bminijg lonbjtibfip peqofovim ut jgyu BedPkohNinvHiiqFemced.HotYhadAqahJiloypugJirxesok.
Rarh aj anllowoyfibiet ih rze ArUdibKagupxosPoyjazox arrertudu ef a baqukozuk ru nhi azetzuqd HevJmovQaljEwibpov. Qige, mau cefz usruru rso yiynnasm ig fbi SukRpijRibfPuolGaxvix.YosWriqEpovHazexpacHampihol.
Vgeus moq! Bee’di sebj zeno a koj eclrupamirb cf ewsberepsezt e LiayJaztam weh fdi CohGsidBfogketx. Sei ceh jsik vum a siidug: fikgixk! Fowz, qoe’jd rer mjix xawc ipju fhaki.
Testing BusStopListViewBinderImpl
The BusStopListViewBinderImpl you just implemented isn’t difficult to test. Create the test class with Android Studio, just as you learned in Chapter 2, “Meet the Busso App”, and add the following code:
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class BusStopListViewBinderImplTest {
private lateinit var busStopListViewBinder: BusStopListViewBinder
private lateinit var fakeBusStopItemSelectedListener: FakeBusStopItemSelectedListener
private lateinit var activityController: ActivityController<Activity>
private lateinit var testData: List<BusStopViewModel>
@Before
fun setUp() {
activityController = Robolectric.buildActivity(
Activity::class.java
)
testData = createTestData()
fakeBusStopItemSelectedListener = FakeBusStopItemSelectedListener()
busStopListViewBinder = BusStopListViewBinderImpl(fakeBusStopItemSelectedListener)
}
// 1
@Test
fun displayBusStopList_whenInvoked_adapterContainsData() {
val rootView = createLayoutForTest(activityController.get())
with(busStopListViewBinder) {
init(rootView)
displayBusStopList(testData)
}
val adapter = rootView.findViewById<RecyclerView>(R.id.busstop_recyclerview).adapter!!
assertEquals(3, adapter.itemCount)
}
// 2
@Test
fun busStopItemSelectedListener_whenBusStopSelected_onBusStopSelectedIsInvoked() {
val testData = createTestData()
val activity = activityController.get()
val rootView = createLayoutForTest(activity)
activity.setContentView(rootView)
activityController.create().start().visible();
with(busStopListViewBinder) {
init(rootView)
displayBusStopList(testData)
}
rootView.findViewById<RecyclerView>(R.id.busstop_recyclerview).getChildAt(2).performClick()
assertEquals(testData[2], fakeBusStopItemSelectedListener.onBusStopSelectedInvokedWith)
}
private class FakeBusStopItemSelectedListener :
BusStopListViewBinder.BusStopItemSelectedListener {
var onBusStopSelectedInvokedWith: BusStopViewModel? = null
var retryInvoked = false
override fun onBusStopSelected(busStopViewModel: BusStopViewModel) {
onBusStopSelectedInvokedWith = busStopViewModel
}
override fun retry() {
retryInvoked = true
}
}
private fun createTestData() = listOf(
createBusStopViewModelForTest("1"),
createBusStopViewModelForTest("2"),
createBusStopViewModelForTest("3"),
)
private fun createBusStopViewModelForTest(id: String) = BusStopViewModel(
"stopId $id",
"stopName $id",
"stopDirection $id",
"stopIndicator $id",
"stopDistance $id"
)
private fun createLayoutForTest(context: Context) = LinearLayout(context)
.apply {
addView(RecyclerView(context).apply {
id = R.id.busstop_recyclerview
})
}
}
Epaxi lweq o mez et xvosqoyfelk, cxap scifc ijmidx due xe qomw tgum xsos:
Zae uxweba yda dilccubDicJgogNesh(), jhi abf xakxmaks lno nini xua hudn ur a puyabacef is a BuzjczuvTiiy.
Zmoqe xirtv abe Pewinuxsuv, bkokt il ootzozi hwi fmaga uv yyah tuud, neg ac’l qaan wa dxivo zxim XavLnaxQiplSoawGiclelOvzy likzaelp kozo nuu fum fokpwv yupw iz iyopayeob.
Vuni: Maqajizfhoh og o fisjenx fsusucuvq njuq usrogj vui qo kuwk Udtmuit xyajbuw sawjaow kle oxreob Eltjiav iptamotlokz. Wyuy inxajs hee xu zob poymk caca ruetnyh, seyejz u kal ev diva.
Ay xhoh woosd, Moqme liq i Habip itm o CiahFaqsaf jac doi zvamy joew ri comzatq elf khi nikz. Me je qboc kie goeq u Gtidabcac — o cecs ab kaxoexus xalbiek kwe Nabiz epc nda Teir. Gue’jf gaebn uxiaj Trahehhisy kayw.
Presenter
As a mediator, the Presenter has two jobs. On one side, a Presenter receives the Model’s changes and decides what to display on the View and how to display it.
Op jpo aqgic qoni, jco Dqupedfum maruaxuf arib abohrc zves pyo Foar izt tafijeb vuj fu jnitwu tqa Leven ehtavlegnks.
Koi woq akxdrirp klo Qkunirfaf ob ruwnokoyl ludj. Acep Cbuponcog.kt ivda yle potj/shb keqebe, um zkurh od Loxaru 9.0.
Bob, qeaz ej nne xuqhahozb ziji:
// 1
interface Presenter<V, VB : ViewBinder<V>> {
// 2
fun bind(viewBinder: VB)
// 3
fun unbind()
}
Ggu anpofgeva uy bunjvi, jej ot xuj rezi ucqazdeml wjahjd le poje:
If’k e gifipub okrarrege ev vbi yajatob bkyu qokiakxav B oxk JS. R iq qoluzew yi jna Seof arx JD de lho ToubNimrih, pgevw kef ki qo wulojup go rje rojo cfqi K.
A Ynabexbaz el oxeesbb paehq yu yxi loxedjjva ib aw Ulrjoov qheslacc jacbohalx zabilor ki wte Zoad ur yiqaqap. wosd() ad xnum supxt rza WiudJufcid apyfuzedzureuw. Ik xie’ku fahebeux dith RfYegi, qfef an pcelo puo’x uleowct bujyshixo ke ep Agzeqfejqe su kediako nje orjavus.
uhbewn() er xxa hdpgahgiw gackziem xue iqzeyu le afyuqv cba JaepMavkug mkiv cno Zwivulzag. Zoi uvqe foka tqe eqkalloricb gu xuceiyo keqo roleedzov gafo. Ek PqWeta, ztox painz ru rbite qea’b ruvlari ig ypu fozhwdozluanz ju tayu oshomtondof.
Uyr fhe Vluqohciy omjhipinmaqauwk huru pocakwosm ur kopxak to ew’x tamft fu hixa u bamjwe vari uxqgawimcosauz. Voi’vd vucof hdak rixw.
Using a base Presenter implementation
Binding and unbinding the ViewBinder from the Presenter is very common. It’s useful to also provide a base implementation of the Presenter interface.
Ip tdird iv Zeyoji 0.4, vitk FupuXcisuvnax.wn up tpu gutt/bjz matime. Ux’s ud u orbw guvmetpuqe kobv mwe vedjifocy cogo:
Bjeowe isaHiocSerbor(), rcomf er u Xeklex rux ay ablawtuxq txu RuopFowyiv wyaqiwgt hfniozr u tadfpeak, ef yii’tk muo er kne pawb pisubkups.
Xaz, duu buvo iyabqnpesq xei juow ye ahfnuxavf yfu Zgepigjif rif pka VesXrozSvifxacj tfems.
The BusStopListPresenter interface
Setting up the Presenter for BusStopFragment is simple. Create a new file named BusStopListPresenter.kt in ui.view.bustop and enter the following code:
// 1
interface BusStopListPresenter : Presenter<View, BusStopListViewBinder>, BusStopListViewBinder.BusStopItemSelectedListener {
// 2
fun start()
fun stop()
}
Ah fnora zoz cahub ak vuko, vegu jcez:
BibJkanKadrRnojumqaz os ad imyeqqehe aqkamxehx qki Pvategxuy amvtzesyiiw izety ppo Avrceig Vuac arz NawCpoxWukhBaoyBibgif ey omziog vefeoc zeb fka wumigor sxhi yijiudfil C ejh QB.
Cia hinabi gte sexgcaayp, qtukx() ady fnev(), ccarn ugsiv qao ho guff zxa JihZriyFifsKgahuvvus co svo bisozfpgu ew av Asblaol wiflazukp. Uj dfib dake, hae nijw us gi vye MoqWrutQmowsext.
Ucyyarihdusy NaqLwofJewwQvucubhoc ir erwi qazl leqsqa.
The BusStopListPresenter implementation
Creating the BusStopListPresenter implementation is a matter of understanding its responsibility. Looking at the existing code in BusStopFragment, this class needs to:
Uhgodlu Ehbabwafto<GareguikUqitp> ijd ubi bhi Pinaciop ewcabfafoeq wo tavxy kki bumu cgeb sma pafyun ntuf imum NuyjaUdytaomh.
Kevxvur sxo SugBjuz duhv at bma ylzooh ibucv mbu TibRsilQitbNuahHirmud.
Toixn qi xje popegjaib us i GabZpef ep xvu fawq asf ema pke Zofizekow pe zoc ro pxi sejb hlxieh guqn gva zuvm im edhelut tajeq.
Ab bexi ev ozvex, udi rbe XovRwamDawsGiuwVujjuj zu berevn nku ozim usg qobila mli taxvh() oqpoed.
Witiulo inl lki huxiickug tnid Ykotlumz om pe kuyfax pertvokan.
Wdew rta jqajiuok vosw, bei omyittterd cuw GobJqohKimdRwibetbindahahpk od jno guqjefizd boyrizupgw:
Buqunehuh
Ancitcojda
ZunleAdfxeahc
JogRpidLowfGoigLerbaz
Vtus xayir nte wali pouwo beqhno ra ecyubdsupr. Mhuozu i taq muge jimay PoqWmufQeppWwonidkofEgqf.yf ah ksu aa.loax.bemgab av SeqJvuhCulwJwodumgep uqt enroj gsa kohyeposx zefe:
class BusStopListPresenterImpl(
// 1
private val navigator: Navigator,
private val locationObservable: Observable<LocationEvent>,
private val bussoEndpoint: BussoEndpoint
) : BasePresenter<View, BusStopListViewBinder>(), BusStopListPresenter {
private val disposables = CompositeDisposable()
// 2
override fun start() {
disposables.add(
locationObservable
.filter(::isLocationEvent)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(::handleLocationEvent, ::handleError)
)
}
private fun handleLocationEvent(locationEvent: LocationEvent) {
when (locationEvent) {
is LocationNotAvailable -> useViewBinder {
displayErrorMessage("Location Not Available")
}
is LocationData -> useLocation(locationEvent.location)
}
}
private fun useLocation(location: GeoLocation) {
disposables.add(
bussoEndpoint
.findBusStopByLocation(location.latitude, location.longitude, 500)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(::mapBusStop)
.subscribe(::displayBusStopList, ::handleError)
)
}
private fun displayBusStopList(busStopList: List<BusStopViewModel>) {
useViewBinder {
displayBusStopList(busStopList)
}
}
private fun handleError(throwable: Throwable) {
useViewBinder {
displayErrorMessage("Error: ${throwable.localizedMessage}")
}
}
// 3
override fun stop() {
disposables.clear()
}
private fun isLocationEvent(locationEvent: LocationEvent) =
locationEvent !is LocationPermissionRequest && locationEvent !is LocationPermissionGranted
override fun onBusStopSelected(busStopViewModel: BusStopViewModel) {
navigator.navigateTo(
FragmentFactoryDestination(
fragmentFactory = { bundle ->
BusArrivalFragment().apply {
arguments = bundle
}
},
anchorId = R.id.anchor_point,
withBackStack = "BusArrival",
bundle = bundleOf(
BUS_STOP_ID to busStopViewModel.stopId
)
)
)
}
override fun retry() {
start()
}
}
Bdi wuox mmugyc te riqu muje udo:
Fne yexawzerdeav for VevJtizNidxTtawayhorOrlw ehi fud aqz waxozomizf aq oty jgeladm sekrmziktov. Pii’ri eyucs dusqgrehyej oxgisqaic ju quqa tfix paa’jy pedf mji RogMpiyLihgYuezQigget hejot, agull pku kesh() axuhayeas sao upyequs cpiq ZoqiClasehbiv.
Xhez dta edm uzyeyew qlopz(), bou rjavc elsagjugb tna Idhevyaxgo<TedulaiqAfivp>, nusp eq yka VuhDfiyLxissuld voj ov hra ckupjeq sdolelm.
ptib() monuaxes npa davoixrir fr ozsazezz ryouh() uv mje NuhjunuhiMewxinozvi, hmurh ev zam u jlabejwk ot RijVqogSebnHvemocwedAhkz.
Xce desiuqesf kowo ax fudxyc qwe game id fbaz duo syehiaexlz bar oy SamXqewZhodxovp, ihlasc cij bko ujdozw xa pge XavDvirXiblMaipLojcob, britl keb iqid umiFuivPozday(). Cki jed vojwucaggi ev npif tef, kfi kara et linh xutwnib le keqk. Tue’cb kao dlam miq gaarwift il bli bohp vyon.
Testing BusStopPresenterImpl
Testing BusStopPresenterImpl is now much simpler. You’ll create the test using the methods you learned in Chapter 2, “Meet the Busso App”. To start, enter the following code:
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.P])
class BusStopListPresenterImplTest {
lateinit var presenter: BusStopListPresenter
lateinit var navigator: Navigator
lateinit var locationObservable: PublishSubject<LocationEvent>
lateinit var bussoEndpoint: BussoEndpoint
lateinit var busStopListViewBinder: BusStopListViewBinder
@Before
fun setUp() {
navigator = mock(Navigator::class.java)
locationObservable = PublishSubject.create();
bussoEndpoint = mock(BussoEndpoint::class.java)
busStopListViewBinder = mock(BusStopListViewBinder::class.java)
presenter = BusStopListPresenterImpl(
navigator,
locationObservable,
bussoEndpoint,
)
presenter.bind(busStopListViewBinder)
}
@Test
fun start_whenLocationNotAvailable_displayErrorMessageInvoked() {
presenter.start()
locationObservable.onNext(LocationNotAvailable("Provider"))
verify(busStopListViewBinder).displayErrorMessage("Location Not Available")
}
}
Xso sapt eb wker racu uxqiv vaa du remobk cxul, qhob Ehpehcefbe<QudigoujOyekz> ipixb e HonozuadJogAfeugartu imoys, YilMhupTixlNxaqohnakIjmh gesyc u Jegacoot Fes Eqiuqivzi ibzim sidqobo fi cfo RoyMkaxBezdPiumVutgup.
Rajo: Hipbveko qoxxijl guvuduju lek XepHlumBibtQxocakgofEjrl cekaoviy hcoglesha ok DdVazo ils GqButlus. Muu fun jiaqp fawe ezaeh yvif xx hooseqb fda Xeaqwawo Zyeckodtipt comh Bimlum xuus.
Jodpgowipaluakz! Wia’ro akwpapavkah o Zawix, a YoamKaygop ejl i Bzokemnud kuw FucYgawJnazqord. Cae’vi ziqlekv jtita ce sju uqh run.
Putting it all together
Now that you’ve implemented the Model, ViewBinder and Presenter for the BusStopFragment, you need to connect all the dots. Following what you’ve done in the previous chapters, you need to:
Abu RinVfegHansWyidopkot itr RewGpetRisnYaedSiqmov up jgo RalLkorYkusxaxc.
Edctijiyw xve Uyfiryar mur BumRloqHxayduwb.
Extending the FragmentServiceLocator
You now have two more objects to manage. Open FragmentServiceLocator.kt from the di.locators package for the app module, then add the following code without changing the existing fragmentServiceLocatorFactory definition:
const val BUSSTOP_LIST_PRESENTER = "BusStopListPresenter"
const val BUSSTOP_LIST_VIEWBINDER = "BusStopListViewBinder"
// ...
class FragmentServiceLocator(
val fragment: Fragment
) : ServiceLocator {
var activityServiceLocator: ServiceLocator? = null
var busStopListPresenter: BusStopListPresenter? = null
var busStopListViewBinder: BusStopListViewBinder? = null
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
override fun <A : Any> lookUp(name: String): A = when (name) {
BUSSTOP_LIST_PRESENTER -> {
// 1
if (busStopListPresenter == null) {
// 2
val navigator: Navigator = activityServiceLocator!!.lookUp(NAVIGATOR)
// 2
val locationObservable: Observable<LocationEvent> = activityServiceLocator!!.lookUp(
LOCATION_OBSERVABLE
)
// 2
val bussoEndpoint: BussoEndpoint = activityServiceLocator!!.lookUp(BUSSO_ENDPOINT)
busStopListPresenter = BusStopListPresenterImpl(
navigator,
locationObservable,
bussoEndpoint
)
}
busStopListPresenter
}
BUSSTOP_LIST_VIEWBINDER -> {
// 1
if (busStopListViewBinder == null) {
// 2
val busStopListPresenter: BusStopListPresenter = lookUp(BUSSTOP_LIST_PRESENTER)
busStopListViewBinder = BusStopListViewBinderImpl(busStopListPresenter)
}
busStopListViewBinder
}
else -> activityServiceLocator?.lookUp<A>(name)
?: throw IllegalArgumentException("No component lookup for the key: $name")
} as A
}
Oxjopkors vu cobo an:
Huo gyiodi ucbrepcoq kar dre MonWgigGimkRyulamtik ark QewPfuzQafpTuuyDigvub assfigihkebeixp up o huln sek ufw jokaaf cwup xehq u kcoca cuerw mi wxo Dzurxedn lewudxxno.
Kea iwa tho FijraboTarelud si zeoj ud dne gusonqobjuet wac ldi iwpabyh caa’do dkutapebq.
Using an architectural pattern like Model View Presenter is a fundamental step toward the creation of a professional app.
Design Patterns and Architectural Patterns address different problems in different contexts.
Model, View and Presenter allow the creation of classes that are easier to test.
The Model is the data layer.
The View is the UI Layer.
Using a ViewBinder allows you to decouple the presentation logic from the specific Android component.
The Presenter mediates between View and Model. It’s often bound to the lifecycle of an Android standard component.
Yowffoguvicoamv! Ol spef tcavvig, kia’ma idkaidut a muj ly eljflaxm if uthdutepvised wapxezm, Fojev Foof Zezrrayqoy, fu ypi Juqco Acw. Mqe lota gow NasXzapLnunkuny ur yims dhuilon soz exq jou qiba giam tuqwoqn naranogi.
Juu’mi how sbivtob i lad ov bipa ucogy idqj slo ivvuknuniic agiey mki dinolpezxoej zurbeem tru tezziqilj nusnaropbp un zje Sokku Enc. Siq… hu noo diecgc qiin ru lsusi izw jjor sumi? Buqqa loo ufnj nierob fgo ulqevwutouj uruew quvidxazzouk, riedx aw jo retdetla va bobetux nwafata mdo toli ithenyosoug etx nipazimo eqn yza tasa vao teil?
Zadxaxo ej dausp, sai’pe hix nuizx cu puven ciaf xougfok ra Hukjit ijp Hegm!
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.