Android Architecture Patterns Part 2:Model-View-Presenter
It’s about time we developers start thinking about how we can apply good architecture patterns in our Android apps. For å hjelpe med Dette, Tilbyr Google Android Architecture Blueprints, Hvor Erik Hellman og jeg jobbet sammen på MVP & rxjava sample. La oss ta en titt på hvordan vi brukte det og fordeler og ulemper med denne tilnærmingen.
her er rollene til hver komponent:
- Modell – datalaget. Ansvarlig for håndtering av forretningslogikk og kommunikasjon med nettverks-og databaselagene.
- Vis-UI-laget. Viser dataene og varsler Presentatøren om brukerhandlinger.Presenter-henter data Fra Modellen, bruker UI-logikken og styrer Tilstanden Til Visningen, bestemmer hva som skal vises og reagerer på brukerinngangsvarsler fra Visningen.
Siden Visningen og Presentatøren jobber tett sammen, må de ha en referanse til hverandre. For Å gjøre Presentatørenheten testbar Med JUnit, er Visningen abstrahert og et grensesnitt for det brukt. Forholdet Mellom Presentatøren og Tilhørende Visning er definert i enContract
grensesnittklasse, noe som gjør koden mer lesbar og forbindelsen mellom de to lettere å forstå.
modellen-view-presenter mønster &rxjava i android arkitektur blåkopier
blueprint prøven er en»å gjøre»program. Det lar en bruker opprette, lese, oppdatere og slette «Å Gjøre» oppgaver, samt bruke filtre til den viste oppgavelisten. RxJava brukes til å flytte av hovedtråden og kunne håndtere asynkrone operasjoner.
Modell
Modellen fungerer med eksterne og lokale datakilder for å hente og lagre dataene. Det er her forretningslogikken håndteres. For eksempel, Når du ber om listen over Task
s, Vil Modellen prøve å hente dem fra den lokale datakilden. Hvis den er tom, vil den spørre nettverket, lagre svaret i den lokale datakilden og deretter returnere listen.
henting av oppgaver gjøres ved Hjelp Av RxJava:
public Observable<List<Task>> getTasks(){
...
}
Modellen mottar som parametere i konstruktør grensesnitt av lokale og eksterne datakilder, Noe Som gjør Modellen helt uavhengig Fra Alle Android klasser og dermed lett å enhet test Med JUnit. For eksempel, for å teste at getTasks
ber om data fra den lokale kilden, implementerte vi følgende test:
@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);
}
Vis
Visningen fungerer med Presentatøren for å vise dataene, og den varsler Presentatøren om brukerens handlinger. I Mvp-Aktiviteter Kan Fragmenter og tilpassede Android-visninger Være Visninger. Vårt valg var Å bruke Fragmenter.
alle Visninger implementerer det samme BaseView-grensesnittet som gjør det mulig å angi En Presentatør.
public interface BaseView<T> {
void setPresenter(T presenter);
}
Visningen varsler Presentatøren om at Den er klar til å bli oppdatert ved å ringe subscribe
Metoden Til Presentatøren i onResume
. Visningen kaller presenter.unsubscribe()
i onPause
for å fortelle Presentatøren at Den ikke lenger er interessert i å bli oppdatert. Hvis implementeringen av Visningen er En Android – tilpasset visning, måsubscribe
ogunsubscribe
– metodene kalles påonAttachedToWindow
ogonDetachedFromWindow
. Brukerhandlinger, som knappeklikk, vil utløse tilsvarende metoder i Presentatøren, dette er den som bestemmer hva som skal skje neste.
Visningene testes Med Espresso. Statistikkskjermen må for eksempel vise antall aktive og fullførte oppgaver. Testen som kontrollerer at dette gjøres riktig, setter først noen oppgaver i TaskRepository
; starter deretterStatisticsActivity
og kontrollerer innholdet i visningene:
@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
Presentatøren og tilhørende Visning opprettes av Aktiviteten. Referanser Til Visningen og til TaskRepository
– Modellen – gis til presentatørens konstruktør. I implementeringen av konstruktøren vil Presentatøren ringesetPresenter
Metoden For Visningen. Dette kan forenkles ved bruk av en avhengighetsinjeksjonsramme som tillater injeksjon Av Presentatørene i de tilsvarende visningene, og reduserer koblingen av klassene. Gjennomføringen Av ToDo-MVP med Dolk er dekket i en annen prøve.
alle Presentatører implementerer Det Samme BasePresenter-grensesnittet.
public interface BasePresenter {
void subscribe();
void unsubscribe();
}
når subscribe
– metoden kalles, Begynner Presentatøren å be om dataene Fra Modellen, så bruker DEN UI-logikken til mottatte data og setter Den til Visningen. For eksempel, i StatisticsPresenter
, blir alle oppgaver forespurt fra TaskRepository
– deretter brukes de hentede oppgavene til å beregne antall aktive og fullførte oppgaver. Disse tallene vil bli brukt som parametere forshowStatistics(int numberOfActiveTasks, int numberOfCompletedTasks)
– metoden I Visningen.
en enhet test for å sjekke at faktisk showStatistics
metoden kalles med de riktige verdiene er enkel å implementere. Vi spotter TaskRepository
og StatisticsContract.View
og gir de spotte objektene som parametere til konstruktøren av et StatisticsPresenter
objekt. Testimplementeringen er:
@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);
}
rollen til unsubscribe
metoden er å fjerne alle abonnementene Til Presentatøren, og dermed unngå minnelekkasjer.
Bortsett frasubscribe
og unsubscribe
, viser Hver Presentatør andre metoder som svarer til brukerhandlingene i Visningen. For eksempelAddEditTaskPresenter
, legger metoder somcreateTask
, som ville bli kalt når brukeren trykker på knappen som oppretter en ny oppgave. Dette sikrer at alle brukerhandlinger – og dermed ALL UI-logikk-går gjennom Presentatøren og dermed kan testes enhet.
Ulemper Med Modell-View-Presenter Mønster
Modellen-View-Presenter mønster bringer med seg en veldig god separasjon av bekymringer. Selv om dette er sikkert en pro, når du utvikler en liten app eller en prototype, kan dette virke som en overhead. For å redusere antall grensesnitt som brukes, fjerner noen utviklereContract
grensesnittklasse og grensesnittet for Presentatøren.En av fallgruvene TIL MVP vises når du flytter UI-logikken til Presentatøren: dette blir nå en allvitende klasse, med tusenvis av kodelinjer. For å løse dette, dele koden enda mer og husk å lage klasser som bare har ett ansvar og er enhetstestbare.
Konklusjon
Modell-View-Controller-mønsteret har to hoved ulemper: for det første Har Visningen en referanse til Både Kontrolleren og Modellen; og for det andre begrenser det ikke håndteringen AV UI-logikk til en enkelt klasse, dette ansvaret deles mellom Kontrolleren og Visningen eller Modellen. Modellen-View-Presenter-mønsteret løser begge disse problemene ved å bryte forbindelsen Som Visningen har Med Modellen og skape bare en klasse som håndterer alt relatert til presentasjonen Av Visningen-Presentatøren: en enkelt klasse som er lett å teste.
Hva om vi ønsker en hendelsesbasert arkitektur, hvor Visningen reagerer på endringer? Følg med for de neste mønstrene samplet I Android Architecture Blueprints for å se hvordan dette kan implementeres. Inntil da, les om Vår Model-View-ViewModel mønster implementering i upday app.