Разширяем код на приложение за Android с MVP, SavePearlHarbor
Още едно копие на пристанището
Главно меню
Навигация на публикации
Разширяем код на приложение за Android с MVP
Тук можете да намерите примерен код, който илюстрира повечето подходи, описани по-долу: https://github.com/remind101/android-arch-sample
Старата школа на Android
Разделянето на притесненията, което включва рамката на Android, е:Модел може да бъде всеки POJO,Изглед е XML маркиране и Фрагмент (или естествено Дейност) действа катоКонтролер/Презентатор. Това работи доста добре на теория, но с нарастването на приложението ви в контролера има много код, свързан с View. Това е така, защото не можете да правите много с XML, така че цялото свързване на данни (свързване на данни), анимации, обработка на входни данни и т.н. се извършва във фрагмента, заедно с бизнес логиката.
Нещата стават още по-лоши, когато сложни елементи на потребителския интерфейс са поставени в списъци или мрежи. Сега е отговорност на адаптера не само да съхранява изгледа и кода на контролера за всички тези елементи, но и да ги управлява като колекция. Тъй като всички тези елементи са плътно свързани, те стават много трудни за поддръжка и още по-трудни за тестване.
Въведете Model-View-Presenter
MVP ни дава възможност да изолираме целия скучен Android код на ниско ниво, който е необходим за показване и взаимодействие с нашия интерфейс вPresentation и да преместим бизнес логиката от по-високо ниво на това, което нашето приложение трябва да прави вPresenter.
За да постигнете това на Android, трябва да третирате дейността или фрагмента като слойизгледи и предоставя лек презентатор за контролиране на изгледа. Най-важното е да се определи отговорността на всеки слой и да се стандартизира интерфейсът между тях. Ето общо описание на разделяне, което работи доста добре за нас:
Изгледът (дейност или фрагмент) е отговорен за:
- Инстанциране на представящия и неговия механизъм за прикачване/откачане;
- Уведомяване на водещия за важни за него събития от жизнения цикъл;
- Съобщение до презентатора за входни събития;
- Поставяне на изгледи и свързването им с данни;
- анимации;
- Проследяване на събития;
- Преминаване към други екрани.
Водещият отговаря за:
- Зареждане на модели;
- Запазване на препратка към модела и състоянието на изгледа;
- Форматиране на това, което трябва да се показва на екрана и указване на изгледа да го покаже;
- Взаимодействие с хранилища (база данни, мрежа и т.н.) (забележка за. Хранилището е модел, за всеки случай);
- Определете какви действия да предприемете, когато входните събития са получени от изгледа.
Ето пример за това как може да изглежда интерфейс между изглед и презентатор:
Има няколко интересни неща, които трябва да имате предвид относно този интерфейс:
Както се установи от практиката, ако кодът на вашия презентатор съдържа Android framework код, а не само чист Java код, вероятно правите нещо нередно. И съответно, ако вижданията ти имат нужда от препратка към модела, явно и ти правиш нещо нередно.
След като се появи въпросът за тестовете,по-голямата част от кода, който трябва да тествате, ще бъде в презентатора. Страхотното е, че този код не се нуждаеAndroid да работи, тъй като има препратки само към интерфейса за изглед, а не към внедряването му в контекста на Android. Това означава, че можете просто да се подиграете с интерфейса на изгледа и да напишете чисти JUnit тестове за бизнес логиката, за да проверите дали методите на подигравания изглед се извикват правилно. [https://github.com/remind101/android-arch-sample/blob/master/app/src/test/java/com/remind101/archexample/presenters/CounterPresenterTest.java] (Така) нашите тестове сега изглеждат така.
Какво ще кажете за списъците?
Досега приемахме, че нашите възгледи са дейности и фрагменти, но в действителност те могат да бъдат всичко. Станахме доста добри в работата със списъци, като имамеViewHolder, който имплементира интерфейса за изглед (както RecyclerView.ViewHolder, така и обикновен стар ViewHolder за използване във връзка с ListView). В адаптера се нуждаете само от основната логика, за да управлявате прикачване/откачане на презентатори (има пример за всичко това в git хранилището).
Ако погледнете примерен екран, съдържащ списък със съобщения, напредък на зареждане и празен изглед, разпределението на отговорността е както следва:
- Представящият списък отговаря за зареждането на съобщенията и логиката за показване на изгледите на списъка/напредъка/празните мъничета;
- Фрагментът е отговорен за внедряването на логиката за показване на изгледи на списък/напредък/заготовки и превключване към други екрани;
- Адаптерът картографира презентаторите към техните ViewHolders;
- Представящият съобщението отговаря за бизнес логиката на отделното съобщение;
- ViewHolder е отговорен за показването на съобщението.
Всички тези компоненти са хлабаво свързани и могат да бъдат тествани отделно един от друг.
Освен това, ако имате екран със списък със съобщения и екранподробности, можете да използвате повторно един и същ презентатор на съобщения и просто да имате две различни реализации на интерфейса на изгледа (във ViewHolder и Fragment). Това запазва вашия код СУХ.
По същия начин персонализираните изгледи могат да реализират интерфейс за изглед. Това ви позволява да използвате MVP в персонализирани джаджи, за да го използвате повторно в различни части на приложението или просто да разделите сложните интерфейси на по-прости блокове.
MVP и преконфигуриране
Ако сте писали на Android известно време, знаете колко трудно е да поддържате промени в ориентацията и конфигурацията:
- Фрагментът/дейностите трябва да могат да възстановят състоянието си. Всеки път, когато работите с фрагмент, трябва да се запитате как това нещо трябва да действа при промяна на ориентацията, какво трябва да се постави в пакета saveInstanceState и т.н.
- Дълго изпълняваните операции на фонови нишки са много трудни за правилни. Една от най-популярните грешки е да се запази препратка към фрагмент/активност във фонова нишка, тъй като трябва да актуализира потребителския интерфейс, след като приключи работата си. Това води до изтичане на памет (и вероятно срив на приложението поради повишена консумация на памет), а също и до факта, че новата активност никога няма да получи обратно извикване и съответно няма да актуализира потребителския интерфейс.
Правилното използване на MVP може да реши този проблем, без изобщо да се налага да мислите за него. Тъй като презентаторите нямат силна препратка към текущия потребителски интерфейс, те са много леки и могат да бъдат възстановени при промени в ориентацията! Тъй като презентаторът запазва препратка към модела и състоянието на изгледа, той може да възстанови правилното състояние на изглед след промяна на ориентацията. Ето грубо описание на това какво се случва, когатозавъртане на екрана, ако се използва този модел:
- Първоначално се създава дейността (да я наречем "първа инстанция");
- Създава се нов презентатор;
- Водещият е прикрепен към дейността;
Как да запазите фрагменти между промените в ориентацията може да се види в хранилището, в класа PresenterManager.
Да, това е краят. Надявам се, че успях да демонстрирам как разделянето на проблеми като MVP ще ви помогне да напишете поддържаем и тестван код.
- Разделете вашата бизнес логика в гол презентиращ java обект;
- Отделете време за създаване на чист интерфейс между вашите презентатори и изгледи;
- Оставете вашите дейности, фрагменти и потребителски изгледи да реализират интерфейса на изгледа;
- За списъци ViewHolder трябва да имплементира интерфейса за изглед;
- Тествайте старателно вашите презентатори;
- Дръжте презентаторите на промяна на ориентацията.