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. Abychom s tím pomohli, Google nabízí plány architektury Android, kde jsme s Erikem Hellmanem spolupracovali na vzorku MVP & RxJava. Podívejme se na to, jak jsme to aplikovali, a na výhody a nevýhody tohoto přístupu.
zde jsou role každé komponenty:
- Model — datová vrstva. Zodpovědný za zpracování obchodní logiky a komunikace s sítě a databázové vrstvy.
- zobrazit-vrstva uživatelského rozhraní. Zobrazí data a upozorní moderátora na akce uživatele.
- Presenter-načte data z modelu, použije logiku uživatelského rozhraní a řídí stav zobrazení, rozhodne, co se má zobrazit a reaguje na oznámení vstupu uživatele z pohledu.
vzhledem k tomu, že View a Presenter úzce spolupracují, musí mít vzájemný odkaz. Aby Presenter unit testovatelné s JUnit, Pohled je získávána a rozhraní pro to používá. Vztah mezi moderátorem a jeho odpovídající Zobrazení je definována v Contract
rozhraní třídy, dělá kód čitelnější a spojení mezi oběma srozumitelnější.
Model-View-Presenter Vzor & RxJava v Android Architektury Plány
plán vzorku je „to Do“ aplikace. To umožňuje uživateli vytvářet, číst, aktualizovat a mazat“ dělat “ úkoly, stejně jako použít filtry do zobrazeného seznamu úkolů. RxJava se používá k přesunu z hlavního vlákna a ke zvládnutí asynchronních operací.
Model
Model pracuje se vzdálenými a lokálními zdroji dat, aby získal a uložil data. Zde se řeší obchodní logika. Například při požadavku na seznam Task
S se Model pokusí načíst je z lokálního zdroje dat. Pokud je prázdný, dotazuje se na síť, uloží odpověď do místního zdroje dat a poté seznam vrátí.
načítání úkolů se provádí pomocí RxJava:
public Observable<List<Task>> getTasks(){
...
}
Model přijímá jako parametry v konstruktoru rozhraní lokální a vzdálené zdroje dat, což je Model zcela nezávislé na jakémkoli Android třídy a tak snadno se jednotka testování s JUnit. Například, test, který getTasks
požaduje data z místního zdroje, provedli jsme následující 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);
}
Zobrazit
Zobrazit pracuje s Presenter pro zobrazení dat a upozorní Moderátor o akce uživatele. V aktivitách MVP mohou být fragmenty a vlastní zobrazení Androidu pohledy. Naše volba byla použít fragmenty.
všechny pohledy implementují stejné rozhraní BaseView, které umožňuje nastavení presenteru.
public interface BaseView<T> {
void setPresenter(T presenter);
}
Zobrazit upozorní Moderátor, že je připraven být aktualizován voláním subscribe
metoda Presenter v onResume
. Zobrazení volá presenter.unsubscribe()
v onPause
, aby oznámilo presenteru, že již nemá zájem o aktualizaci. Pokud provádění Pohledu je Android custom view, pak subscribe
unsubscribe
metody musí být volán na onAttachedToWindow
onDetachedFromWindow
. Akce uživatele, jako kliknutí na tlačítko, spustí odpovídající metody v presenteru, což je ten, který rozhodne, co se má stát dál.
pohledy se testují pomocí espressa. Na obrazovce statistik je například třeba zobrazit počet aktivních a dokončených úkolů. Test, který zkontroluje, že je to provedeno správně první staví některé úkoly v TaskRepository
; pak spustí StatisticsActivity
a kontroly obsahu zobrazení:
@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()));
}
Moderátor
Presenter a jeho odpovídající Zobrazení jsou vytvářeny Činnost. Odkazy na View a na TaskRepository
– Model – jsou dány konstruktoru presenteru. Při implementaci konstruktoru Presenter zavolá metodu zobrazení setPresenter
. To lze zjednodušit při použití rámce pro vstřikování závislostí, který umožňuje vstřikování Presenterů do odpovídajících pohledů, což snižuje spojení tříd. Implementace ToDo-MVP s dýkou je pokryta jiným vzorkem.
všechny Presentery implementují stejné rozhraní BasePresenter.
public interface BasePresenter {
void subscribe();
void unsubscribe();
}
Když subscribe
metoda je volána, Moderátor začne požadovat data z Modelu, pak platí UI logika přijatých dat a nastaví ji na Pohled. Například v StatisticsPresenter
, jsou všechny úkoly požadované od TaskRepository
– pak obnovené úkoly jsou použita pro výpočet počtu aktivních a dokončených úkolů. Tato čísla budou použita jako parametry pro metodu zobrazení showStatistics(int numberOfActiveTasks, int numberOfCompletedTasks)
.
test jednotky, který zkontroluje, zda je metoda showStatistics
volána se správnými hodnotami, je snadno implementovatelná. Jsme zesměšňovat TaskRepository
StatisticsContract.View
a dát posmívali objekty jako parametry konstruktoru StatisticsPresenter
objekt. Implementace testu je:
@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);
}
úlohou metody unsubscribe
je vymazat všechny odběry presenteru, čímž se zabrání úniku paměti.
kromě subscribe
a unsubscribe
každý Presenter vystavuje jiné metody, které odpovídají akcím uživatele v zobrazení. Například AddEditTaskPresenter
, přidá metody, jako je createTask
, to by být volána, když uživatel stiskne tlačítko, které vytvoří nový úkol. Tím je zajištěno, že všechny akce uživatele – a následně veškerá logika uživatelského rozhraní-procházejí Presenterem a mohou být testovány jednotkou.
nevýhody vzoru Model-View-Presenter
vzor Model-View-Presenter s sebou přináší velmi dobré oddělení obav. I když je to určitě profesionál, při vývoji malé aplikace nebo prototypu se to může zdát jako režie. Chcete-li snížit počet použitých rozhraní, někteří vývojáři odstraní třídu rozhraní Contract
a rozhraní pro Presenter.
jedna z úskalí MVP se objeví při přesunu logiky UI do presenteru: toto se nyní stává vševědoucí třídou s tisíci řádků kódu. Chcete-li to vyřešit, rozdělte kód ještě více a nezapomeňte vytvořit třídy, které mají pouze jednu odpovědnost a jsou testovatelné jednotkami.
závěr
vzor Model-View-Controller má dvě hlavní nevýhody: za prvé, pohled má odkaz na řadič i Model; a za druhé, neomezuje manipulaci s logikou UI na jednu třídu, přičemž tato odpovědnost je sdílena mezi řadičem a pohledem nebo modelem. Model-View-Presenter pattern řeší oba tyto problémy tím, že rozbije připojení, které Zobrazení má s Modelem a vytváří pouze jednu třídu, která zpracovává vše, co souvisí s předložením View — Presenter: jediné třídy, která je snadné, aby unit test.
Co když chceme architekturu založenou na událostech, kde pohled reaguje na změny? Zůstaňte naladěni na další vzory vzorkované v plánech architektury Android, abyste zjistili, jak to lze implementovat. Do té doby si přečtěte o naší implementaci modelu-View-ViewModel pattern v aplikaci upday.