Presenter
May 26, 2020 · View on GitHub
Presenter
Содержат всю логику соответствующего экрана. Все презентеры должны быть унаследованы от класса BasePresenter.
Презентеры не уничтожаются при изменении конфигурации.
Этот механизм реализован с помощью сохранения всего Dagger-компонента
экрана и переиспользования его для воссозданного View.
Презентер уничтожается при полном уничтожении экрана (например после Activity#finish()).
Для оповещения вью об изменения состояния используется одна модель
(extends ScreenModel), которая передается в метод вью #render().
Когда View становится готовой к работе, у презентера вызываются методы:
-
#onFirstLoad()- первый запуск экрана - первичная загрузка данных; -
#onLoad()- запуск экрана - действия при каждой загрузке. В данный метод приходит флагviewRecreated, который говорит о том, открыт ли экран заново либо пересоздан из-за смены конфигурации;
Презентер имеет методы для отслеживания жизненого цикла View. Также здесь
можно зарегистрировать делегатов в screenEventDelegateManager (см. Делегаты).
Presenter может взаимодействовать с асинхронными задачами только через Rx.
Для подписки на асинхронную задачу следует вызвать один из методов презентера #subscribe().
Подписка, созданная таким образом, обладает следующими свойствами:
-
Происходит заморозка Rx событий (
onNext,onError,onComplete) перед уничтожением View и разморозка после ее воссоздания. Это свойство необходимо для предотвращении обработки событий, когда Presenter не имеет ссылки на View. -
Происходит заморозка Rx событий на
#onPauseи разморозка на#onResume. Это свойство позволяет предотвращать обработку событий невидимыми экранами (например, когда экран находится в стеке). -
Происходит отписка всех Rx подписок при полном уничтожении экрана.
Передача в Presenter аргумента, переданного в Intent или Bundle (которые
обернуты в Route),
при старте экрана должна производиться через Dagger.
Для сетевых запросов в презентере предусмотрены методы BasePresenter#subscribeIoHandleError(),
содержащие стандартный обработчик ошибок сетевых запросов.
При этом обработчик ошибки можно заменить, переписав метод BasePresenter#handleError().
Все варианты методов #subscribe() перед подпиской переводят Observable
в mainThread (.observeOn(...)).
Кроме того, все варианты методов #subscribeIoHandleError()
производят подписку на Observable в рабочем потоке (.subscribeOn(...)).
Также к слою Presenter относятся классы с логикой UI части приложения,
такие как Navigator( см. Навигация).
Эти классы также поставляются Dagger и не должны иметь прямую ссылку на Activity,
View и другие части, которые могут стать невалидными при смене конфигурации.
Однако для доступа к функционалу этих частей можно использовать классы FragmentProvider и
ActivityProvider, которые поставляют валидные Fragment и Activity при каждом вызове #get().
Api презентера может иметь только методы вида void someMethod(params...),
это необходимо для соответствия принципу unidirectional data flow

Обработка ошибок
Ошибки с запросов к репозиторию обрабатываются в презентере.
Для этого предусмотрен механизм, с возможностью поставить стандартный обработчик ошибок на все запросы. Он предоставляется модулем core-mvp.
Для этого необходимо реализовать интерфейс ErrorHandler и предоставить
ее как зависимость, например, через даггер:
@Module
class ErrorHandlerModule {
@Provides
@PerScreen
fun provideNetworkErrorHandler(standardErrorHandler: StandardErrorHandler): ErrorHandler {
return standardErrorHandler
}
}
Конкретную реализацию можно посмотреть здесь и здесь.
Данный обработчик будет автоматически перехватывать ошибки при использовании
метода BasePresenter#subscribeIoHandleError.
Частные случаи и установку состояния экрана при ошибке следует производить
в коллбеках метода BasePresenter#subscribeIo и BasePresenter#subscribeIoHandleError.
Например так:
stopCampaignDisposable = subscribeIoHandleError(postInteractor.stopPromoCampaign(screenModel.postId),
{
view.showPromotionStopped()
},
{
screenModel.loadState = LoadState.ERROR
view.render(screenModel)
})
Автоматическа перезагрузка данных при появлении соединения с сетью
BasePresenter предоставляет механизм для автоматической перезагрузки данных,
в случае, когда пропало соединение с сетью, а потом появилось.
Этот механизм представлен методами BasePresenter#subscribe...AutoReload().
Лучшие практики
Для объединения запросов к репозиториям необходимо использовать
ObservableUtil#combineLatestDelayError() вместо Observable#zip().
Это продиктовано особенностью получения данных.
Каждый Observable может эмитить 2 элемента: один из кеша, другой с сервера. Observables в этом случае выполняются параллельно.