- Что такое единый стейт? Если экран сложный, то как описывать сложные стейты. Если через seald class, то по какому принципу описывать seald class со стейтом?
- Как работать с single liveData (map/distinct?)
- Как работать с command liveData? Нужно разделить общие команды и приватные для каждого экрана.
- Как подружить liveData и state-delegate? Какую сделать структуры обертки для загружаемых данных(Loading, Content, Error).
- Как обрабатывать пересоздание диалогов (как подсунуть новую лямбду) Нужно посмотреть сюда.
- Использовать: mapDistinct
- Использовать: viewBinding
- Делать маппинг данных к Item для группи во ViewModel, чтобы ViewModel ничего не знала об item'ах
- Название публичных методов ViewModel в информирубщем стиле (
onLoginClicked
)
Если использовать VM + livedata то возникает проблема с синхронизацией нескольких LiveData. Поэтому приходится использовать единый стейт и единственный метод его отрисовки render(state)
. Этот паттерн назвается Model-View-Update (MVU) и основан он на Unidirectional data flow (UDF). Есть несколько реализаций UDF. Самые популярные - это TEA(The Elm Architectire), MVI, Redux и Flux. И многие ребята которые о них рассказывают сами путаются в различиях.
Во всех из них используется единый стейт и сайд эффект/команда/евент (везде называется по разному, но суть SingleEventLiveData).
Единый стейт удобно тестировать, но при его использовании вздникают другие проблемы:
-
Как обновлять единый стейт? Если View передает все дейтвия во ViewModel/Presenter, то обновление стейта может проихожить во многих местах. Из-за этого, на сложных экранах, появляются состояния гонки и новый стейт может перезатереть другой новый стейт.
Решений этой проблемы 2:-
Конвертировать все действия в единую шину (Action) и на основе этой шины делать reducer, который применяет действие с Action в стейт.
- Плюсы: все изменения в одном месте из проще читать и понимать.
- Минусы: очень много кода получается, в котором особой логики нет. Можно использовать генераторы кода, например actions-dispatcher, но тогда время компиляции увеличивается и это грустно.
-
Написать потокобезопасную утилиту обновления стейта, в которой сначало всегда будет обновление стейта, а потом получение.
- Плюсы: Руки развязаны, можно обновлять где угодно. Кода меньше.
- Минусы: Стейты можно обновлять где угодно, нет четкого контроля. Можно написать плохо.
-
Сайд еффект - это одноразовое событие: навигация, отображение snackbar, диалога и обработка интентов (шаринг, intent chooser и т.п.).
Основная проблема при использовании сайд эффектов это дублирование кода. Какие-то сайд эффекты являются общими для всех экранов, а какие-то нет. Решение - вынесение работы с сайд эффектами в базовый класс и переопределение обработчика локально, там где это требуется.
Для изменения состояний экрана у нас используется state-delegator. Он позволяет объединить несколько view в группу и менять их состояния 1 строчкой. Ну и самое важное - это возможность добавления анимации перехода из одного состояния в другое.
Мне на моем проекте недостаточно было набора состояний Loading Content Error
, поэтому я расширил этот набор и получил следующие состояния (ViewState):
Content, Progress
- используется для отправки запроса на сервер (авторизация).Loading, Content, Error
- получение данных от сервера, где отсутвует состояние отсутвия данных.Loading, Content, Stub, Error
- Получение данных от сервера, где нужна обработка состояния отсутвия данных.Content, Error
- получение данных из кэша, где состояние загрузки не требуется, но требуется обработка ошибок.Content, Stub, Error
- получение данных из кэша, где состояние загрузки не требуется, но требуется обработка ошибок и отсутвия данных.Shimmer, Content, Progress, Stub, Error
- Самый сложный набор состояний, в которм идет получение данных с сервера и отправка изменненых данных на вервер. По сути это объединение 1 и 3 набора состояний.
В этом подходе есть 2 проблемы:
- В каждом варианте может присутствовать pull to refresh, который усложнаяет логику работы с ViewState.
- Появляется 2 стейте ViewState (delegator) и ViewState (Данные во VM) xD. Их можно попробовать объединить, но тогда VM начинает зависить от состояния view, что не очень хорошо.
- Некоторые экран тербуют использования recycler-base подхода. То есть весь экран реализован в виде списка. Из-за этого происходит смещение подходов обычного использования view со state-delegator и частичного его использования в связке с RecyclerView.
-
Сергей Рябов — Как приготовить хорошо прожаренный MVI под Android
-
MVI от Hannes Dorfmann
-
Оригинальный цикл из 8-ми статей
- Hannes Dorfmann: Основы MVI
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part1 - Model
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part2 - View and Intent
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part3 - State Reducer
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part4 - Independent UI Components
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part5 - Debugging with ease
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part6 - Restoring State
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part7 - Timing (SingleLiveEvent problem)
- Hannes Dorfmann: Reactive Apps with Model-View-Intent - Part8 - Navigation
-
Перевод от Тинькофф (пока только 3 статьи)
-
-
Raywenderlich: MVI Architecture for Android Tutorial: Getting Started
- Все тайны MVI (Интервью от одного из авторов MVICore)
- Аналог MVICore от того же автора. Проще чем MVICore
- Современная MVI-архитектура на базе Kotlin
- Архитектурный шаблон MVI в Kotlin Multiplatform, часть 1
- Видео пример того как отлаживать приложения с IntelliJ IDEA "MVIKotlin time travel" plugin
- Android LiveData на Kotlin с использованием Retrofit и coroutines
- Effective LiveData and ViewModel Testing
- LiveData & ViewModel — Making your own magic
- LiveData with Coroutines and Flow (Android Dev Summit '19)
- When to load data in ViewModels
- Architecture Components: Easy Mapping of Actions and UI State
- LiveData with single events
- Android: Understand LiveData
- ViewModel и LiveData: паттерны и антипаттерны
- События на базе LiveData Android
- ViewModels and LiveData: Patterns + AntiPatterns
- Designing and Working with Single View States on Android
- PresentationModel
- Redux против MobX без путаницы
- пример от Ромы
- пример работы с mapDistinct
- ViewModel and LiveData extensions (немного скорректированные гисты из базового проекта)
- экстеншены для ViewBinding + делегат
- Example MVI implementation, based off of Google's architectural samples.
- android-showcase
- workflow-kotlin
- Pokedex
- ModelWatcher
- oolong - MVU for Kotlin Multiplatform
- actions-dispatcher - кодогенерация для MVI.
- file-template-loader - Плагин позволяет упростить создание шаблонов для проектов, которые разрабатываются с помощью IDE от JetBrains: Android Studio, IntelliJ IDEA и т.д.
- Tinder StateMachine - A Kotlin DSL for finite state machine.
- RxRedux by Freeletics
- MvRx by Airbnb
- Mobius by Spotify