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. För att hjälpa till med detta erbjuder Google Android-arkitekturritningar, där Erik Hellman och jag arbetade tillsammans på MVP & RxJava-provet. Låt oss ta en titt på hur vi tillämpade det och fördelarna och nackdelarna med detta tillvägagångssätt.

här är rollerna för varje komponent:

  • Modell — datalagret. Ansvarig för hantering av affärslogik och kommunikation med nätverks-och databaslagren.
  • Visa-UI-lagret. Visar data och meddelar presentatören om användaråtgärder.
  • presentatör-hämtar data från modellen, tillämpar UI-logiken och hanterar vyens tillstånd, bestämmer vad som ska visas och reagerar på användarinmatningsmeddelanden från vyn.

eftersom vyn och presentatören arbetar nära varandra måste de ha en hänvisning till varandra. För att göra Presentatörsenheten testbar med JUnit abstraheras vyn och ett gränssnitt för den används. Förhållandet mellan presentatören och dess motsvarande vy definieras i en Contract gränssnittsklass, vilket gör koden mer läsbar och anslutningen mellan de två lättare att förstå.

Modell-view-presentatör klassstruktur

model-view-presentatör mönster&RxJava i Android arkitektur ritningar

Blueprint provet är en ”att göra” ansökan. Det låter en användare skapa, läsa, uppdatera och ta bort ”att göra” uppgifter, samt tillämpa filter på den visade listan över uppgifter. RxJava används för att flytta bort huvudgängan och kunna hantera asynkrona operationer.

Modell

modellen fungerar med fjärr-och lokala datakällor för att hämta och spara data. Det är här affärslogiken hanteras. När du till exempel begär listan över Tasks, skulle modellen försöka hämta dem från den lokala datakällan. Om det är tomt kommer det att fråga nätverket, spara svaret i den lokala datakällan och sedan returnera listan.

hämtningen av uppgifter görs med hjälp av RxJava:

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

modellen får som parametrar i konstruktörgränssnittet för de lokala och avlägsna datakällorna, vilket gör modellen helt oberoende av alla Android-klasser och därmed lätt att enhetstest med JUnit. Till exempel, för att testa att getTasks begär data från den lokala källan, genomförde vi följande 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);
}

Visa

vyn fungerar med presentatören för att visa data och det meddelar presentatören om användarens åtgärder. I MVP-aktiviteter kan fragment och anpassade Android-vyer vara vyer. Vårt val var att använda fragment.

alla vyer implementerar samma BaseView-gränssnitt som gör det möjligt att ställa in en presentatör.

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

vyn meddelar presentatören att den är redo att uppdateras genom att ringasubscribepresentatörens metod ionResume. Vyn anropar presenter.unsubscribe()I onPause för att berätta för presentatören att den inte längre är intresserad av att uppdateras. Om implementeringen av vyn är en Android-anpassad vy, måstesubscribe ochunsubscribe metoder anropasonAttachedToWindow ochonDetachedFromWindow. Användaråtgärder, som knappklick, utlöser motsvarande metoder i presentatören, det här är den som bestämmer vad som ska hända nästa.

vyerna testas med Espresso. Statistikskärmen måste till exempel visa antalet aktiva och slutförda uppgifter. Testet som kontrollerar att detta görs korrekt sätter först några uppgifter i TaskRepository; startar sedan StatisticsActivity och kontrollerar innehållet i vyerna:

@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()));
}

presentatör

presentatören och dess motsvarande vy skapas av aktiviteten. Hänvisningar till vyn och till TaskRepository – modellen – ges till presentatörens konstruktör. Vid genomförandet av konstruktören kommer presentatören att ringa setPresenter – metoden för vyn. Detta kan förenklas när man använder ett ramverk för beroendeinjektion som möjliggör injektion av presentatörerna i motsvarande vyer, vilket minskar kopplingen av klasserna. Genomförandet av ToDo-MVP med dolk omfattas av ett annat prov.

alla presentatörer implementerar samma BasePresenter-gränssnitt.

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

Närsubscribe – metoden kallas börjar presentatören begära data från modellen, då tillämpar den UI-logiken på den mottagna data och ställer in den i vyn. Till exempel, i StatisticsPresenter, begärs alla uppgifter från TaskRepository – då används de hämtade uppgifterna för att beräkna antalet aktiva och slutförda uppgifter. Dessa siffror kommer att användas som parametrar för showStatistics(int numberOfActiveTasks, int numberOfCompletedTasks) metod för vyn.

ett enhetstest för att kontrollera attshowStatistics – metoden anropas med rätt värden är lätt att implementera. Vi hånar TaskRepository och StatisticsContract.View och ger de hånade objekten som parametrar till konstruktören av ett StatisticsPresenter – objekt. Testimplementeringen är:

@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 förunsubscribe – metoden är att rensa alla prenumerationer på presentatören och därmed undvika minnesläckor.

bortsett från subscribe och unsubscribe, exponerar varje presentatör andra metoder som motsvarar användaråtgärderna i vyn. Till exempel lägger AddEditTaskPresenter till metoder som createTask, som skulle kallas när användaren trycker på knappen som skapar en ny uppgift. Detta säkerställer att alla användaråtgärder – och därmed all UI – logik-går igenom presentatören och därmed kan enhetstestas.

nackdelar med Model-View-presentatör mönster

Model-View-presentatör mönster för med sig en mycket god separation av oro. Även om detta är säkert ett proffs, när man utvecklar en liten app eller en prototyp, detta kan verka som en overhead. För att minska antalet gränssnitt som används tar vissa utvecklare bort gränssnittsklassen Contract och gränssnittet för presentatören.

en av fallgroparna i MVP visas när du flyttar UI-logiken till presentatören: detta blir nu en allvetande klass med tusentals kodrader. För att lösa detta, dela upp koden ännu mer och kom ihåg att skapa klasser som bara har ett ansvar och är enhetstestbara.

slutsats

modellen-View-Controller mönstret har två huvudsakliga nackdelar: för det första har vyn en hänvisning till både styrenheten och modellen; och för det andra begränsar det inte hanteringen av UI-logik till en enda klass, detta ansvar delas mellan styrenheten och vyn eller modellen. Modellen-visa-Presentatörsmönstret löser båda dessa problem genom att bryta anslutningen som Vyn har med modellen och skapa bara en klass som hanterar allt relaterat till presentationen av vyn — presentatören: en enda klass som är lätt att enhetstesta.

vad händer om vi vill ha en händelsebaserad arkitektur, där vyn reagerar på förändringar? Håll ögonen öppna för nästa mönster samplade i Android arkitektur ritningar för att se hur detta kan genomföras. Fram till dess, läs om vår modell-View-ViewModel-mönsterimplementering i upday-appen.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *