Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Проблемы с управлением State в UI? Тогда мы идем к вам!

Проблемы с управлением State в UI? Тогда мы идем к вам!

Дмитрий Бердников, Redmadrobot – MOSDROID #16 Sulfur

История о том, как в быстро растущем приложении обычный MVP/MVVM вставлял палки в колеса, какие проблемы возникали с отображением итогового UI и как мы решили их благодаря просмотру видео с Jake Wharton (внезапно) и щепотки MVI. Никакой рекламы — только практический опыт.

MOSDROID

March 30, 2019
Tweet

More Decks by MOSDROID

Other Decks in Programming

Transcript

  1. В роботах с 2017 года, в разработке 4,5+ года. Работал

    на проектах: «Plazius», «Мой Билайн», «Билайн ШПД», «Согласие МРМ» и не только ;) Сейчас на проекте «Открытие МСБ». Из интересов: архитектура, проектирование, процессы, soft skills, хайп-девелопмент (нет). Обо мне
  2. О чем расскажу Redmadrobot Щепотку в сторону MVI 01 02

    03 Немножечко об RxJava Кейс нашего приложения Выводы 04
  3. Интерфейс Transformer принимает входящий поток с одним типом и на

    выход отдает другой (опционально). Работает в паре с оператором compose RxJava Observable Transformer Redmadrobot
  4. Работает схожим образом, что и reduce()/fold() в Kotlin. Кэширует последний

    эмит, который проходил через цепочку RxJava Оператор scan() Redmadrobot
  5. RxJava Оператор scan() Redmadrobot .scan(InitialState()) { state, action -> //

    Обновляем state на основе action } Action()
  6. Одно из свойств этого оператора позволяет, распределить эмиты, поступаемые сверху

    цепочки, между подписчиками, не создавая дубликатов RxJava Оператор publish() Redmadrobot
  7. Фарш требований 01 Прокачаем пины Надо группировать заведения по удаленности

    от юзера. Если гео нет, то брать его из доп. запроса 02 Добавим превью карточек с изыском Надо сделать “карусельку" заведений на карте и чтобы иконочки анимировали свой масштаб при скроле 03 Добавим анимацию на самой карте Просто скролить “карусельку” не прикольно, надо еще и карту анимировать, чтобы центрировался пин заведения 04 Блин, забыли про пользователя! Нам крайне важно трепать, где находится пользователь, пускай его обновляется каждые 10 минут и сверяется с ближайшими заведениями, вдруг он окажется в 50 м от него, тогда надо показать другую карточку (да ладно?) Redmadrobot Кейс 05 Может редизайн?
  8. Фарш требований 01 Прокачаем пины Надо группировать заведения по удаленности

    от юзера. Если гео нет, то брать его из доп. запроса 02 Добавим превью карточек с изыском Надо сделать “карусельку" заведений на карте и чтобы иконочки анимировали свой масштаб при скроле 03 Добавим анимацию на самой карте Просто скролить “карусельку” не прикольно, надо еще и карту анимировать, чтобы центрировался пин заведения 04 Блин, забыли про пользователя! Нам крайне важно трепать, где находится пользователь, пускай его обновляется каждые 10 минут и сверяется с ближайшими заведениями, вдруг он окажется в 50 м от него, тогда надо показать другую карточку (да ладно?) Redmadrobot Кейс 05 Может редизайн?
  9. Managing State with RxJava Пример State Redmadrobot data class MainState(

    val data: List<Item>?, val isLoading: Boolean, val error: Throwable? )
  10. 01 Опишите ваш State 02 Определите набор Actions Redmadrobot Как

    начать управлять состоянием с RxJava
  11. Managing State with RxJava Пример набора Actions Redmadrobot sealed class

    MainAction : Action { class GetStrings(val creditId: Int) : MainAction() object StartLoading : MainAction() object ErrorLoading : MainAction() class StringsLoaded(val strings: List<String>) : MainAction() }
  12. 01 Опишите ваш State 02 Определите набор Actions 03 Опишите

    обработку Actions Redmadrobot Как начать управлять состоянием с RxJava
  13. Cхема потока данных Redmadrobot Notify ViewModel ViewModel LiveData<State/Events> Handler ActionsHandler

    Observable<Action> send Actions/Events send Actions Observable<Action> View Create new State (reduce)
  14. Managing State with RxJava Пример Handler Redmadrobot private fun createGetDataHandler():

    ObservableTransformer<MainAction.GetData, Action> { return ObservableTransformer { actions -> actions.switchMap { repository.getStrings() .map<Action>(MainAction::DataLoaded) .startWith(MainAction.Loading) .onErrorReturn(MainAction::ErrorLoading) } } }
  15. 01 Опишите ваш State 02 Определите набор Actions 03 Опишите

    обработку Actions 04 Описываем Reducer Redmadrobot Как начать управлять состоянием с RxJava
  16. Пример Reducer Redmadrobot fun reduce(state: MainState, action: Action): MainState {

    return when (action) { MainAction.Loading -> { state.copy(isLoading = true) } is MainAction.ErrorLoading -> { state.copy(isLoading = false, error = action.error) } is MainAction.DataLoaded -> { state.copy(isLoading = false, data = mapToUiData(action.data)) } else -> state } }
  17. 01 Опишите ваш State 02 Определите набор Actions 03 Опишите

    обработку Actions 04 Описываем Reducer Redmadrobot 05 Упаковываем все вместе. Profit! Как начать управлять состоянием с RxJava
  18. Корневая подписка Redmadrobot actionsSource .observeOn(Schedulers.io()) .compose { actions -> actionsHandler.bindHandlers(actions)

    } .scan(initialState) { state, action -> reduce(state, action) } .distinctUntilChanged() .subscribe(stateSource::accept) abstract fun reduce(state: S, action: Action): S
  19. Корневая подписка Redmadrobot actionsSource .observeOn(Schedulers.io()) .compose { actions -> actionsHandler.bindHandlers(actions)

    } .scan(initialState) { state, action -> reduce(state, action) } .distinctUntilChanged() .subscribe(stateSource::accept) abstract fun reduce(state: S, action: Action): S
  20. Корневая подписка Redmadrobot actionsSource .observeOn(Schedulers.io()) .compose { actions -> actionsHandler.bindHandlers(actions)

    } .scan(initialState) { state, action -> reduce(state, action) } .distinctUntilChanged() .subscribe(stateSource::accept) abstract fun reduce(state: S, action: Action): S
  21. Выводы 01 Повышенный порог вхождения и непривычно 02 Нужно описывать

    Actions/State/Handlers 03 Отлично подходит, где сложная логика с множеством состояний 04 Есть проблема (задача) с перерисовкой Redmadrobot 05 Классно поддерживать 06 Чем дольше используешь этот подход, тем естественней думать в этом разрезе 07 Для простых экранов может быть оверкил 08 Стоит иметь в арсенале
  22. Решение Overdraw Redmadrobot @BindingAdapter(“android:text") public static void setText(TextView view, CharSequence

    text) { final CharSequence oldText = view.getText(); if (text == oldText || (text == null && oldText.length() == 0)) { return; } if (text instanceof Spanned) { if (text.equals(oldText)) { return; // No change in the spans, so don't set anything. } } else if (!haveContentsChanged(text, oldText)) { return; // No content changes, so don't set anything. } view.setText(text); }
  23. Что почитать/посмотреть Redmadrobot https://youtu.be/0IKHxjkgop4 Jake Wharton https://github.com/rcmkt/managing-state-with-rxjava- sample Базовый пример

    https://proandroiddev.com/unidirectional-data-flow-on- android-the-blog-post-part-1-cadcf88c72f5 Цикл статей про Redux в Android https://medium.com/appnroll-publication/build-your-own- mvi-framework-a76d72c6e8e7 Пример реализации