Современная библиотека для организации слоя представления, следующая идеалогии UDF.
Является продолжением и развитием идей библиотеки Surf-MVI
Основная идея в том, что у экрана есть единое состояние.
Логика изменения этого состояния отделена от логики запросов в сеть или базу данных.
Все, что происходит на экране это события.
Ответственность за изменение состояния лежит строго на отдельной сущности - reducer’е. То есть изменять состояние экрана не может никто, кроме reducer’а.
Ответственность за трансформацию событий лежит на middleware.
Таким образом вся логика, приводящая к изменению состояния UI концентрируется в одном месте и она отделена от логики запросов к серверу, в базу данных, получению каких-то обновлений с других экранов итд. А вью остается только отрисовывать это состояние и отправлять события взаимодействия с пользователем.
В 2020 стэк android отдела был полностью завязан на RxJava и все проекты, стартовавшие в студии использовали MVI для организации слоя представления. И библиотека, которая для этого использовалась была полностью основана на реактивных потоках, реализованных с помощью RxJava.
В 2021 году стартовало обновление стэка android отдела. И в будущем планируется уйти полностью от RxJava и в новых проектах использовать корутины и Flow. Но от MVI отказываться не хотелось, поэтому я перенес идеи, которые были заложены и реализованы в библиотеке mvi-surf в библиотеку mvi-flow. Сущности и их ответственности остались все те же самые.
- Reducer - получает на вход событие и на основе этого события возвращает новое состояние (или старое, если событие не требует изменения состояния UI).
- Middleware - трансформирует потоки событий. Например событие клика по кнопке в событие начала сетевого запроса.
- View - подписывается на изменения состояния и изменяется в соответствие с ними.
- EventHub - основная и единственная шина событий, происходящих на экране.
- ScreenBinder - объединяет все сущности для совместной работы. Подписывает Middleware на события экрана для их трансформации, трансформированные события отправляет в EventHub, а также уведомляет редьюсер о каждом новом событии и хабе.
Одной из главных фишек библиотеки Surf-MVI был DSL, позволяющий описывать трансформации событий в лаконичном стиле используя средства котлина.
mvi-flow уже подерживает почти все DSL-трансформации к которым мы уже привыкли
- map - трансформация события в событие
- eventMap - трансформация события в поток событий
- streamMap - трансформация потока событий в поток событий
- react - простая реакция на событие
//TODO
- декомпозиция событий
- фильтрация событий в DSL
В эпоху, когда все писали приложения используя MVP и выполняя команды на вью меняя его состояние очень легко было попасть в ситуацию, когда какой-то блок кода приводит вью в состояние, которое не соответствует ожидаемому.
На простых экранах это редко происходило, но чем сложнее становилась логика презентера - тем чаще начинали возникать эти ситуации.
Например обычным делом было вынести отдельно команды отображения и скрытия загрузчика на вью. И в начале выполнения какого-то запроса выполнять команду отображения, а при завершении запроса выполнять команду скрытия. Но что если на экране несколько запросов? А что если какой-то из них завершается с ошибкой? Или один завершится быстрее другого? Или есть один из них начнет выполнение позже другого? В каком порядке выполнятся эти команды?
Аналогично реализации MVVM, рекомендованной гуглом, когда у вьюмодели есть несколько Observable полей по типу isLoading: StateFlow, isError: StateFlow.
А сама вьюмодель при этом во время работы экрана меняет флаги в этих состояниях.
Чем сложнее становится логика экранов тем сложнее поддерживать консистентное состояние вью вызывая на ней нужные команды в случае MVP (часто важен даже порядок их вызова) или изменяя несколько Observable состояний.
Отделение логики изменения вью от бизнес-логики позволяет создавать очень сложные экраны. При этом не приходится работать с огромным презентером на несколько тысяч строк, который содержит большое количество логики и чем сложнее эта логика - тем более хрупким становится код в нем.
Реализацию простого экрана с полным набором сущностей и классов можно посмотреть в модуле sample
Telegram: @RuslanSharipov
Gmail: [email protected]