Articles

Android Architecture Patterns Part 2:Model-View-Presenter

Florina Muntenescu
Florina Muntenescu

Follow

Nov 2, 2016 · 6 min read

It’s about time we developers start thinking about how we can apply good architecture patterns in our Android apps. Avuksi tähän Google tarjoaa Android-arkkitehtuurin pohjapiirustuksia, joissa teimme Erik Hellmanin kanssa yhdessä MVP & rxjavan näytteen. Katsotaanpa, miten sovelsimme sitä ja tämän lähestymistavan hyviä ja huonoja puolia.

tässä ovat jokaisen komponentin roolit:

  • malli — datakerros. Vastaa liiketoimintalogiikan käsittelystä ja yhteydenpidosta verkko-ja tietokantatasoihin.
  • Näytä — KÄYTTÖLIITTYMÄKERROS. Näyttää tiedot ja ilmoittaa juontajalle käyttäjän toimista.
  • Presenter — hakee tiedot mallista, soveltaa käyttöliittymän logiikkaa ja hallitsee näkymän tilaa, päättää mitä näytetään ja reagoi käyttäjän syöteilmoituksiin näkymästä.

koska katsoja ja juontaja tekevät tiivistä yhteistyötä, heillä täytyy olla viittaus toisiinsa. Jotta juontaja yksikkö voidaan testata JUnit, näkymä on abstrakti ja käyttöliittymä sitä käytetään. Esittäjän ja sitä vastaavan näkymän suhde on määritelty Contract rajapintaluokassa, mikä tekee koodista luettavamman ja näiden kahden välisestä yhteydestä helpommin ymmärrettävän.

malli-view-presenter class structure

the model-view-presenter pattern&RxJava in Android architecture blueprints

the blueprint sample is a ”to do” application. Sen avulla käyttäjä voi luoda, lukea, päivittää ja poistaa ”to Do” – tehtäviä sekä käyttää suodattimia näytettyyn tehtäväluetteloon. Rxjavaa käytetään pääkierteen irrottamiseen ja asynkronisten operaatioiden hoitamiseen.

malli

malli toimii etätietolähteiden ja paikallisten tietolähteiden avulla tietojen saamiseksi ja tallentamiseksi. Tässä käsitellään bisneslogiikkaa. Esimerkiksi pyydettäessä luetteloa Tasks, Malli yrittäisi hakea ne paikallisesta tietolähteestä. Jos se on tyhjä, se kyselee verkosta, tallentaa vastauksen paikalliseen tietolähteeseen ja palauttaa listan.

tehtävien haku tapahtuu Rxjavan avulla:

public Observable<List<Task>> getTasks(){ 
...
}

malli saa parametreina paikallisten ja etätietolähteiden rakentajan rajapinnoissa, jolloin malli on täysin riippumaton kaikista Android-luokista ja siten helppo yksikkötestata Junitilla. Esimerkiksi testataksemme, että getTasks pyytää tietoja paikallisesta lähteestä, toteutimme seuraavan testin:

@Mock 
private TasksDataSource mTasksRemoteDataSource;
@Mock
private TasksDataSource mTasksLocalDataSource;
...
@Test
public void getTasks_requestsAllTasksFromLocalDataSource() {
// Given that the local data source has data available
setTasksAvailable(mTasksLocalDataSource, TASKS);
// And the remote data source does not have any data available
setTasksNotAvailable(mTasksRemoteDataSource);
// When tasks are requested from the tasks repository
TestSubscriber<List<Task>> testSubscriber =
new TestSubscriber<>();
mTasksRepository.getTasks().subscribe(testSubscriber); // Then tasks are loaded from the local data source
verify(mTasksLocalDataSource).getTasks();
testSubscriber.assertValue(TASKS);
}

View

näkymä toimii juontajan kanssa tietojen näyttämiseksi ja ilmoittaa juontajalle käyttäjän toimista. MVP-toiminnassa fragmentit ja muokatut Android-näkymät voivat olla näkymiä. Meidän valintamme oli käyttää sirpaleita.

kaikki näkymät toteuttavat saman BaseView-käyttöliittymän, joka mahdollistaa juontajan asettamisen.

public interface BaseView<T> {
void setPresenter(T presenter);
}

näkymä ilmoittaa juontajalle olevansa valmis päivitykseen kutsumalla subscribeesittäjän metodi onResume. The View kutsuu presenter.unsubscribe() in onPause kertomaan juontajalle, ettei ole enää kiinnostunut päivityksestä. Jos näkymän toteutus on Android custom view, niin subscribe ja unsubscribe menetelmiä on kutsuttava onAttachedToWindow ja onDetachedFromWindow. Käyttäjän toimet, kuten napin painallukset, käynnistävät vastaavat menetelmät juontaja, tämä on yksi, joka päättää, mitä pitäisi tapahtua seuraavaksi.

näkymiä testataan espressolla. Esimerkiksi tilastonäytön on näytettävä aktiivisten ja suoritettujen tehtävien määrä. Testi, joka tarkistaa, että tämä tehdään oikein, asettaa ensin joitakin tehtäviä TaskRepository, käynnistää sitten StatisticsActivity ja tarkistaa näkymien sisällön:

@Before 
public void setup() {
// Given some tasks
TasksRepository.destroyInstance();
TasksRepository repository = Injection.provideTasksRepository( InstrumentationRegistry.getContext()); repository.saveTask(new Task("Title1", "", false));
repository.saveTask(new Task("Title2", "", true)); // Lazily start the Activity from the ActivityTestRule
Intent startIntent = new Intent();
mStatisticsActivityTestRule.launchActivity(startIntent);
}@Test
public void Tasks_ShowsNonEmptyMessage() throws Exception {
// Check that the active and completed tasks text is displayed
Context context = InstrumentationRegistry.getTargetContext();
String expectedActiveTaskText = context
.getString(R.string.statistics_active_tasks);
String expectedCompletedTaskText = context
.getString(R.string.statistics_completed_tasks); onView(withText(containsString(expectedActiveTaskText)))
.check(matches(isDisplayed()));
onView(withText(containsString(expectedCompletedTaskText)))
.check(matches(isDisplayed()));
}

Presenter

juontaja ja sitä vastaava näkymä luodaan toiminnalla. Viittaukset näkymään ja TaskRepository – malli – annetaan juontajan rakentajalle. Konstruktorin toteutuksessa juontaja kutsuu setPresenter method of the View. Tätä voidaan yksinkertaistaa, kun käytetään riippuvuusruiskutuskehystä, joka mahdollistaa juontajien ruiskutuksen vastaavissa näkymissä, jolloin luokkien kytkentä vähenee. ToDo-MVP: n toteutus tikarilla käsitellään toisessa otoksessa.

kaikki juontajat toteuttavat saman BasePresenter-rajapinnan.

public interface BasePresenter {
void subscribe();
void unsubscribe();
}

kun subscribe menetelmä kutsutaan, juontaja alkaa pyytää tietoja mallista, sitten se soveltaa KÄYTTÖLIITTYMÄLOGIIKKAA vastaanotettuun dataan ja asettaa sen näkymään. Esimerkiksi StatisticsPresenter kaikki tehtävät pyydetään TaskRepository – silloin haettuja tehtäviä käytetään laskemaan aktiivisten ja suoritettujen tehtävien määrä. Näitä lukuja käytetään parametreinashowStatistics(int numberOfActiveTasks, int numberOfCompletedTasks) näkymän metodi.

yksikkötesti, jolla voidaan tarkistaa, että itse asiassa showStatistics – menetelmää kutsutaan oikeilla arvoilla, on helppo toteuttaa. Pilkkaamme TaskRepository ja StatisticsContract.View ja annamme pilkkatut objektit parametreiksi StatisticsPresenter objektin rakentajalle. Testin toteutus on:

@Test 
public void loadNonEmptyTasksFromRepository_CallViewToDisplay() {
// Given an initialized StatisticsPresenter with
// 1 active and 2 completed tasks
setTasksAvailable(TASKS); // When loading of Tasks is requested
mStatisticsPresenter.subscribe(); // Then the correct data is passed on to the view
verify(mStatisticsView).showStatistics(1, 2);
}

unsubscribe menetelmä on tyhjentää kaikki juontajan tilaukset, jolloin vältetään muistivuodot.

lukuun ottamatta subscribe ja unsubscribe jokainen juontaja paljastaa muita menetelmiä, jotka vastaavat käyttäjän toimia näkymässä. Esimerkiksi AddEditTaskPresenter, lisää menetelmiä kuten createTask, jota kutsutaan, kun käyttäjä painaa nappia, joka luo uuden tehtävän. Näin varmistetaan, että kaikki käyttäjän toimet – ja siten kaikki käyttöliittymän logiikka – menevät läpi juontaja ja siten voidaan yksikkötestata.

Model-View-Presenter-kuvion haitat

Model-View-Presenter-kuvio tuo mukanaan erittäin hyvän huolien erottelun. Vaikka tämä on varmasti ammattilainen, kun kehitetään pieni sovellus tai prototyyppi, tämä voi tuntua overhead. Käytettyjen rajapintojen määrän vähentämiseksi jotkut kehittäjät poistavat Contract käyttöliittymäluokan, sekä käyttöliittymän juontajalle.

yksi MVP: n sudenkuopista näkyy, kun UI-logiikkaa siirretään juontajalle: tästä tulee nyt kaikkitietävä luokka, jossa on tuhansia rivejä koodia. Tämän ratkaisemiseksi Jaa koodi vielä enemmän ja muista luoda luokkia, joilla on vain yksi vastuu ja jotka ovat yksikkötestattavissa.

johtopäätös

Mallinäkymä-ohjain-kuviolla on kaksi pääasiallista haittapuolta: ensinnäkin näkymässä on viittaus sekä ohjaimeen että malliin; ja toiseksi, se ei rajoita käyttöliittymän logiikan käsittelyä yhteen luokkaan, vaan tämä vastuu jaetaan ohjaimen ja näkymän tai mallin välillä. Malli-näkymä — juontaja-malli ratkaisee nämä molemmat ongelmat katkaisemalla näkymän ja mallin välisen yhteyden ja luomalla vain yhden luokan, joka käsittelee kaiken näkymän esittämiseen liittyvän-juontajan: yhden luokan, joka on helppo yksikkötesti.

Mitä jos halutaan tapahtumapohjainen arkkitehtuuri, jossa näkymä reagoi muutoksiin? Pysy kuulolla seuraavan malleja näyte Android Arkkitehtuuri piirustukset nähdä, miten tämä voidaan toteuttaa. Siihen asti, lukea meidän malli-View-view model pattern toteutus upday app.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *