Разработанный нами с Юрой фреймворк Moxy получил широкое распространение в разработке под Android. Он обеспечивает реализацию MVP паттерна при работе с Activity, Fragment и View, полностью отделяя “callback hell” их жизненного цикла от презентера.
Данное поведение реализовано за счет сущности ViewState, которая проксирует вызовы методов между Presenter и View, при этом сохраняя некоторые из них в очереди на основании специальных стратегий. При пересоздании View вызываются не все методы, а только те, которые находятся в очереди на данный момент.
В данной статье мы расскажем, как работают стратегии, предоставляемые “из коробки”, и в каких случаях стоит применять каждую из них.
Принципы работы Moxy
Очередь команд (методы, которые вызываются у view) в презентере определяют состояния View после пересоздания.
Сохранение команд в очереди происходит согласно стратегии, указанной для нее в методе интерфейса View. Сам ViewState генерируется во время компиляции на основании интерфейса View и используемых в нем аннотаций.
Неаккуратное использование стратегий может повлечь неправильное поведение приложения и переполнения памяти
Команды могут применяться ко View только когда оно находится в активном состоянии – в промежутке времени между вызовами методов getMvpDelegate().onAttach() и getMvpDelegate().onDetach() из Activity, Fragment или кастомной android.view.View.
Устройство схемы
Для иллюстрации работы стратегий мы будем использовать следующую схему:
Ось X представляет собой временную шкалу (направление времени слева-направо).
Кружками обозначены команды, одинаковые команды (соответственно имеющие одинаковую стратегию) обозначены кружками с одинаковыми цветом и цифрой.
На оси презентера отображены команды, которые инициирует презентер методом getViewState().doSomething(args). Команда, выполняемая презентером, имеет ту стратегию, которая рассматривается в данной схеме, если не указано обратного.
Ось ViewState представляет собой состояние очереди команд, которые применяются ко View при его последующем пересоздании. Команды применяются последовательно, от нижних к верхним. Исходное состояние ViewState отображается в крайнем левом положении на оси.
Ось View представляет собой набор команд, которые применились ко View. Значок смены ориентации означает пересоздание View. Ось View может начинаться с середины диаграммы – это означает что View перешло в активное состояние именно с этого момента.
Стратегии из коробки
Moxy по умолчанию предоставляет следующие стратегии:
AddToEndStrategy — выполнить команду и добавить команду в конец очереди
AddToEndSingleStrategy — выполнить команду, добавить ее в конец очереди и удалить все ее предыдущие экземпляры
SingleStateStrategy — выполнить команду, очистить очередь и добавить в нее команду
SkipStrategy — выполнить команду
OneExecuteStrategy — выполнить команду при первой возможности
Рассмотрим подробнее как они работают и когда их стоит применять.
AddToEndStrategy
AddToEndStrategy — самая простая стратегия, представляющая собой добавление команды в конец очереди:
При вызове презентером команды (2) со стратегией AddToEndStrategy:
- Команда (2) добавляется в конец очереди ViewState
- Команда (2) применяется ко View, если оно находится в активном состоянии
При пересоздании View:
- Ко View последовательно применяются команды из очереди ViewState
AddToEndSingleStrategy
Данная стратегия отличается от AddToEndStrategy тем, что в очереди ViewState может находиться только один экземпляр каждой команды, помеченной данной стратегией.
Расширенное использование
При использовании кастомных команд можно изменять состояние очереди (к примеру, дублировать любую из имеющихся команд), однако делать это следует с большой осторожностью)
При вызове презентером команды (2) со стратегией AddToEndStrategy:
- Команда (2) добавляется в конец очереди ViewState.
- В случае, если в очереди уже находилась команда (2), старая команда удаляется из очереди
- Команда (2) применяется ко View, если оно находится в активном состоянии
При пересоздании View:
- Ко View последовательно применяются команды из очереди ViewState
SingleStateStrategy
Данная стратегия по своему принципу работы похожа на AddToEndSingleStrategy. Отличие заключается в том, что при попадании во ViewState команды с данной стратегией, очередь команд очищается полностью.
При вызове презентером команды (1) со стратегией SingleStateStrategy:
- Полностью очищается очередь команд
- Команда (1) добавляется в конец очереди ViewState.
- Команда (1) применяется ко View, если оно находится в активном состоянии
При пересоздании View:
- Ко View последовательно применяются команды из очереди ViewState
SkipStrategy
Skip стратегия не меняет стека ViewState. Команда применяется ко View, только в случае, если оно находится в активном состоянии.
Команда ведет себя по-разному, в зависимости от наличия вью в активном состоянии.
а) В случае наличия View в активном состоянии:
При вызове презентером команды (4) со стратегией SkipStrategy, в случае наличия View в активном состоянии:
- Команда применяется ко View
- Очередь команд остается неизменной
При пересоздании View:
- Ко View последовательно применяются команды из очереди ViewState
б) В случае отсутствия View в активном состоянии:
При вызове презентером команды (4) со стратегией SkipStrategy, в случае отсутствия View в активном состоянии:
- Команда не применяется ко View
- Очередь команд остается неизменной
При пересоздании View:
- Ко View последовательно применяются команды из очереди ViewState
OneExecuteStrategy
Данная стратегия очень похожа на SkipStrategy. Отличие заключается в том, что при отсутствии view в активном состоянии, команда дожидается ее появления, а затем применяется ровно один раз.
Команда ведет себя по-разному, в зависимости от наличия вью в активном состоянии.
а) В случае наличия View в активном состоянии:
При вызове презентером команды (4) со стратегией OneExecuteStrategy, в случае наличия View в активном состоянии (Поведение полностью аналогично SkipStretegy):
- Команда применяется ко View
- Очередь команд остается неизменной
При пересоздании View:
- Ко View последовательно применяются команды из очереди ViewState
б) В случае отсутствия View в активном состоянии:
При вызове презентером команды (4) со стратегией OneExecuteStrategy, в случае отсутствия View в активном состоянии:
- Команда (4) добавляется в конец очереди ViewState
При появлении View в активном состоянии:
- Ко View последовательно применяются команды из очереди ViewState
- При выполнении команды (4) она удаляется из очереди команд. При последующем пересоздании View команда (4) не вызовется.
Отсутствие стратегии у метода
Отсутствие явного указания стратегии у метода ведет к ее автоматическому выводу на основании стратегии для всего интерфейса и стратеги по умолчанию. В настоящее время стратегией по умолчанию является AddToEndStrategy.
Область применения команд
В данном разделе мы собрали типичные случаи применения команд.
AddToEndStrategy — применяется в случае, когда нужно последовательно применить несколько команд, которые должен быть повторно применены при пересоздании View.
Пример: Экран организации состоит из 3-х блоков: общая информации, акции, история покупок. Данные части могут быть загружены и отображены на экране асинхронно. После пересоздания важно, отобразить информацию во всех блоках. Все методы будут помечены стратегией AddToEndStrategy
AddToEndSingleStrategy — применяется в случае, когда команда должна применяться при пересоздании view не более одного раза.
Пример: На экране есть индикатор загрузки, видимость которого меняется вызовом метода View: toggleLoading(visible: Boolean).
Cпойлер: здесь и далее примеры кода приведены на Kotlin.
Данный метод будет иметь стратегию AddToEndSingleStrategy.
SingleStrategy — применяется в случае, если нам не важен результат команд отработавших до нее.
Пример: Данные экрана профиля подгружаются из сети. Экран имеет 3 взаимоисключающие состояния: загрузка, отображение данных и экран—заглушка. Соответственно View имеет методы showLoading(), showData() и showStub(). Все три команды будут иметь стратегию SingleStateStrategy.
SkipStrategy — используется в случае, если нам необходимо выполнить некоторое действие, прямо сейчас и только в случае, если имеется View в активном состоянии.
Пример 1: Имеется экран редактирования данных пользователя. При нажатии кнопки сохранить, отображается индикатор загрузки посредством вызова команды toggleLoading(show = true). При неуспешной загрузке экран возвращается в исходное состояние командой toggleLoading(show = false) и отображается SnackBar с информацией об ошибке командой showLoadingError(). Последняя команда будет иметь стратегию SkipStrategy, т.к. результат ее выполнения нужен только при активном экране и не должен сохраняться при смене конфигурации.
Пример 2: Старт анимации после какого-то действия пользователя. Команду имеет смысл применять только один раз, причем при отсутствии активной вью не требуется сохранение ее в очередь.
OneExecuteStrategy — используется в случае, если нам необходимо выполнить некоторое действие при первом появление View в активном состоянии.
Пример 1: Открытие следующего экрана. Запуск нового Activity, Fragment или FragmentDialog удобно выполнять посредством данной стратегии. В этом случае мы будем иметь гарантированно 1 запуск.
Уточнение
Если быть точнее, гарантированный запуск при первом аттаче View. Если по каким-то причинам View было не в активном состоянии и в активное состояние так и не пришло, то вызова команды не произойдет. Данная ситуация теоретически возможна, нужно иметь это ввиду.
OneExecuteStrategy и SkipStrategy очень похожи, будьте внимательны при их использовании.
Как избежать проблем из-за стратегий?
При использовании стратегий вы можете столкнуться с двумя видами проблем:
Проблема 1. Лишние команды в очереди
При использовании только AddToEndStrategy, которая является стратегией по умолчанию, очередь постепенно увеличивается. Это может вызывать:
- Freeze ui в момент прикрепления View к Presenter
- Переполнение по памяти
Решение:
Старайтесь минимизировать величину очереди команд, посредством выставления стратегий.
Проблема 2. Неправильное состояние при пересоздании view
Зачастую приложение требует только портретную ориентации, вследствии чего при разработке она фиксируется, однако пересоздание вью происходит не только при смене ориентации, но и при других сменах конфигурации.
Решение:
При разработке оставлять возможность смены ориентации приложения. Это поможет вам контролировать не только правильность применения стратегий, но и остальные side-эффекты смену конфигурации устройства.
Заключение
Стратегии в Moxy являются довольно гибким инструментом, неаккуратное использование которого может повлечь логические ошибки. Надеемся что данная статья сделает использование Moxy более прозрачным и очевидным.
В следующей статье мы рассмотрим, как создавать кастомные стратегии.
Пусть ваш код всегда выполняется так, как вы его проектировали!
от авторов библиотеки Moxy Xanderblinov и senneco
Материалы по теме
Поделиться с друзьями
Комментарии (8)
terrakok
13.04.2017 17:12+1Наш фреймворк Moxy
Так-то он детище компании Arello Mobile в первую очередь в лице senneco и комьюнити, а блог красного_робота запутает любого!
Не надо так :)terrakok
13.04.2017 18:17zagayevskiy
13.04.2017 19:25+1Интересное кино… А блог меня обманул, что это их разработка, в статьях так и пишут.
Xanderblinov, прокомментируете?Xanderblinov
13.04.2017 21:56+1Создатели библиотеки я и senneco это известная информация. Я работаю в REDMADROBOT, поэтому статья публикуется от блога компании — не вижу каких то противоречий
mefi100fell
Отличная статья.
Для тех, кто до сих пор задается вопросами о стратегиях, все разжевано и в рот положено.
Пользуюсь Moxy больше года, рад, как удав.