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. Om hiermee te helpen, biedt Google Android Architecture Blueprints, waar Erik Hellman en ik samenwerkten aan de MVP & RxJava sample. Laten we eens kijken naar hoe we het toegepast en de voor-en nadelen van deze aanpak.

Hier zijn de rollen van elke component:

  • Model-de gegevenslaag. Verantwoordelijk voor het omgaan met de bedrijfslogica en de communicatie met de netwerk-en databaselagen.
  • beeld-de UI-laag. Geeft de gegevens weer en informeert de presentator over de acties van de gebruiker.
  • Presenter-haalt de gegevens uit het Model, past de UI-logica toe en beheert de status van de weergave, bepaalt wat er wordt weergegeven en reageert op invoermeldingen van gebruikers vanuit de weergave.

omdat de weergave en de presentator nauw samenwerken, moeten ze naar elkaar verwijzen. Om de presentator unit testbaar te maken met JUnit, wordt de weergave geabstraheerd en wordt er een interface voor gebruikt. De relatie tussen de presentator en de bijbehorende weergave is gedefinieerd in een Contract interfaceklasse, waardoor de code leesbaarder wordt en de verbinding tussen de twee gemakkelijker te begrijpen is.

Model-View-Presenter-klasse structuur

Het Model-View-Presenter Patroon & RxJava in Android Architectuur Blauwdrukken

De blauwdruk steekproef is een ‘To Do’ – applicatie. Het laat een gebruiker maken, lezen, bijwerken en verwijderen “te doen” taken, evenals filters toepassen op de weergegeven lijst met taken. RxJava wordt gebruikt om van de hoofddraad af te bewegen en asynchrone bewerkingen te kunnen verwerken.

Model

het Model werkt met de externe en lokale gegevensbronnen om de gegevens te verkrijgen en op te slaan. Dit is waar de business logica wordt behandeld. Bijvoorbeeld, wanneer het Model de lijst van Tasks opvraagt, zou het proberen om ze op te halen uit de lokale gegevensbron. Als het leeg is, zal het het netwerk opvragen, het antwoord opslaan in de lokale gegevensbron en vervolgens de lijst retourneren.

het ophalen van taken wordt gedaan met behulp van RxJava:

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

het Model ontvangt als parameters in de constructorinterfaces van de lokale en externe gegevensbronnen, waardoor het Model volledig onafhankelijk is van alle Android-klassen en dus eenvoudig te testen is met JUnit. Bijvoorbeeld, om te testen dat getTasks gegevens opvraagt van de lokale bron, hebben we de volgende test geà mplementeerd:

@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

De View werkt samen met de presentator om de gegevens weer te geven en het informeert de presentator over de acties van de gebruiker. In MVP-activiteiten kunnen fragmenten en aangepaste Android-weergaven worden bekeken. Onze keuze was om fragmenten te gebruiken.

alle weergaven implementeren dezelfde baseview interface die het mogelijk maakt een presentator in te stellen.

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

De weergave meldt de presentator dat het klaar is om te worden bijgewerkt door de subscribemethode van de presentator aan te roepen in onResume. De weergave roept presenter.unsubscribe() aan in onPause om de presentator te vertellen dat het niet langer geïnteresseerd is om te worden bijgewerkt. Als de implementatie van de weergave een aangepaste Android-weergave is, moeten de subscribe en unsubscribe methoden worden aangeroepen op onAttachedToWindow en onDetachedFromWindow. Gebruikersacties, zoals klikken op de knop, zullen overeenkomstige methoden in de presentator activeren, dit is degene die bepaalt wat er vervolgens moet gebeuren.

de weergaven worden getest met Espresso. Het statistiekenscherm moet bijvoorbeeld het aantal actieve en voltooide taken weergeven. De test die controleert of dit correct is uitgevoerd plaatst eerst enkele taken in de TaskRepository; start vervolgens de StatisticsActivity en controleert de inhoud van de weergaven:

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

presentator

de presentator en de bijbehorende weergave worden gemaakt door de activiteit. Verwijzingen naar de weergave en naar de TaskRepository – het Model – worden gegeven aan de constructeur van de presentator. Bij de implementatie van de constructor zal de presentator de setPresenter methode van de weergave aanroepen. Dit kan worden vereenvoudigd bij het gebruik van een dependency injection framework dat de injectie van de presentatoren in de bijbehorende weergaven mogelijk maakt, waardoor de koppeling van de klassen wordt verminderd. De uitvoering van de ToDo-MVP met dolk wordt behandeld in een ander monster.

alle presentatoren implementeren dezelfde basispresenter interface.

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

wanneer de subscribe methode wordt aangeroepen, begint de presentator de gegevens van het Model aan te vragen, dan past het de UI-logica toe op de ontvangen gegevens en stelt het in op de weergave. Bijvoorbeeld, in de StatisticsPresenter, worden alle taken gevraagd van de TaskRepository – dan worden de opgehaalde taken gebruikt om het aantal actieve en voltooide taken te berekenen. Deze getallen worden gebruikt als parameters voor deshowStatistics(int numberOfActiveTasks, int numberOfCompletedTasks) methode van de weergave.

een unit test om te controleren of de showStatistics methode wordt aangeroepen met de juiste waarden is eenvoudig te implementeren. We bespotten de TaskRepository en de StatisticsContract.View en geven de bespotte objecten als parameters aan de constructor van een StatisticsPresenter object. De testimplementatie is:

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

de rol van de unsubscribe methode is om alle abonnementen van de presentator te wissen, waardoor geheugenlekken worden vermeden.

behalve subscribe en unsubscribe, toont elke presentator andere methoden die overeenkomen met de acties van de gebruiker in de weergave. Bijvoorbeeld, de AddEditTaskPresenter, voegt methoden toe zoals createTask, die zouden worden aangeroepen wanneer de gebruiker op de knop drukt die een nieuwe taak maakt. Dit zorgt ervoor dat alle gebruikersacties – en dus alle UI – logica-door de presentator gaan en daardoor unit getest kunnen worden.

nadelen van Model-View-Presenter patroon

Het Model-View-Presenter patroon brengt een zeer goede scheiding van zorgen met zich mee. Hoewel dit is zeker een pro, bij het ontwikkelen van een kleine app of een prototype, dit kan lijken op een overhead. Om het aantal gebruikte interfaces te verminderen, verwijderen sommige ontwikkelaars de Contract interfaceklasse, en de interface voor de presentator.

een van de valkuilen van MVP verschijnt bij het verplaatsen van de UI-logica naar de presentator: dit wordt nu een alwetende klasse, met duizenden regels code. Om dit op te lossen, splits de code nog meer en vergeet niet om klassen die slechts één verantwoordelijkheid hebben en zijn unit testable te creëren.

conclusie

Het Model-View-Controller patroon heeft twee belangrijke nadelen: ten eerste heeft de View een verwijzing naar zowel de Controller als het Model; en ten tweede, het beperkt de behandeling van UI logica niet tot een enkele Klasse, deze verantwoordelijkheid wordt gedeeld tussen de Controller en de weergave of het Model. Het Model-View-Presenter-patroon Lost beide problemen op door de verbinding die de weergave heeft met het Model te verbreken en slechts één klasse te creëren die alles met betrekking tot de presentatie van de weergave afhandelt — de Presenter: een enkele klasse die gemakkelijk te testen is.

Wat als we een op gebeurtenissen gebaseerde architectuur willen, waarbij de weergave reageert op Wijzigingen? Stay tuned voor de volgende patronen gesampled in de Android architectuur blauwdrukken om te zien hoe dit kan worden geïmplementeerd. Tot die tijd, lees over onze Model-View-ViewModel patroon implementatie in de upday app.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *