Skip to content

Instantly share code, notes, and snippets.

@Shipaaaa
Last active January 25, 2024 10:31
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Shipaaaa/772a9899c22c7fe37d55436b42af7791 to your computer and use it in GitHub Desktop.
Save Shipaaaa/772a9899c22c7fe37d55436b42af7791 to your computer and use it in GitHub Desktop.

Материалы для вхождения в MVI

Открытые вопросы

  • Что такое единый стейт? Если экран сложный, то как описывать сложные стейты. Если через 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).

Работа с единым стейтом

Единый стейт удобно тестировать, но при его использовании вздникают другие проблемы:

  1. Как обновлять единый стейт? Если View передает все дейтвия во ViewModel/Presenter, то обновление стейта может проихожить во многих местах. Из-за этого, на сложных экранах, появляются состояния гонки и новый стейт может перезатереть другой новый стейт.
    Решений этой проблемы 2:

    1. Конвертировать все действия в единую шину (Action) и на основе этой шины делать reducer, который применяет действие с Action в стейт.

      • Плюсы: все изменения в одном месте из проще читать и понимать.
      • Минусы: очень много кода получается, в котором особой логики нет. Можно использовать генераторы кода, например actions-dispatcher, но тогда время компиляции увеличивается и это грустно.
    2. Написать потокобезопасную утилиту обновления стейта, в которой сначало всегда будет обновление стейта, а потом получение.

      • Плюсы: Руки развязаны, можно обновлять где угодно. Кода меньше.
      • Минусы: Стейты можно обновлять где угодно, нет четкого контроля. Можно написать плохо.

Работа с сайд эффектами

Сайд еффект - это одноразовое событие: навигация, отображение 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 проблемы:

  1. В каждом варианте может присутствовать pull to refresh, который усложнаяет логику работы с ViewState.
  2. Появляется 2 стейте ViewState (delegator) и ViewState (Данные во VM) xD. Их можно попробовать объединить, но тогда VM начинает зависить от состояния view, что не очень хорошо.
  3. Некоторые экран тербуют использования recycler-base подхода. То есть весь экран реализован в виде списка. Из-за этого происходит смещение подходов обычного использования view со state-delegator и частичного его использования в связке с RecyclerView.

Список литературы и полезных материалов

Сторонние решения

Model-View-Update (MVU)

Unidirectional data flow (UDF)

Model-View-Intent (MVI)

MVI от Badoo

TEA (The Elm Architecture)

Прочие решения

Примеры

Примеры с других проектов

Сторонние примеры

Полезные утилиты

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment