Как разделить Android Activity или фрагменты и состояние приложения?
В наших android проектах практикуется переход между фрагментами или activity через intent при котором одно activity вызывает другой или один фрагмент инициирует запуск другого фрагмента. В итоге каждый фрагмент должен знать о следующем фрагменте и о данных которые он должен передать.
Я хочу переделать такую архитектуру чтобы был только объект который хранит текущее состояние пользователя, например на какой форме он находится, его данные, чтобы объект сохранял свое состояние при onPause и чтобы фрагменты или activity всегда могли построится из этого объекта даже если приложение свернуто или закрыто. И чтобы фрагменты или activity ничего не знали о других формах.
Существуют ли примеры такой архитектуры?
Или фраймворки которые уже следуют такой методологии?
Или как вы строите такую архитектуру?
Создаем обертку для стейт-машины, в которую есть возможность передать событие. ПРодумываем сохранение и восстановление состояния
Используем Singleton или Dependecy Injection для получения ссылки на нашу обертку
Прописываем состояния и события стейт-машины
Создаем интерфейс для подписки на стейт-машину, в котором есть метод возвращающий фрагмент или интент
Каждый фрагмент или активити наследуются от базового, который имплементирует интерфейс подписчика. В обработчики стартуем пришедший интент или фрагмент
В самой активити и фраменте отправляем стейт-машине события
Интересная штучка, я в одном из проектов тоже в итоге к машине состояний пришёл, только к самописной. Потенциал переиспользования в новых проектах у неё довольно слабый, но зато проблемы с инкапсуляцией кода, относящегося к различным состояниям UI, она решает отлично.
Мне нравится именно в этой библиотеке то, что одно и то же событие может приводить в разные состояния в зависимости от того, в каком состоянии сейчас находится машина. Я сталкивался с библиотеками в которых событие всегда приводит к одному состоянию. И мне это не очень понравилось, так как теряется гибкость. Ну и очень удобно в этой библиотеке описать Flow, иметь несколько Flow и переключаться между ними. И не менее удобно то, что можно стартануть машину в любое промежуточное состояние. Т.е. отработать проверку какого-то условия и решить на старте в какое состояние нам нужно перевести машину. Это как раз из вопроса о том, как сохранять/восстанавливать состояния.
Владимир Якушев: Есть такая MVP библиотека - Mosby. Там есть понятие ViewState. Смысл в том, чтобы в ViewState хранить состояние View, а presenter занимался бы только бизнес логикой. hannesdorfmann.com/mosby/viewstate Я так понимаю что ViewState это и есть стэйт машина?
Стейт-машина - это другой термин для конечных автоматов: есть состояние как вершины графа, и есть события как пути между графами. В данной библиотеке ViewState похож на стейт-машину, но реализован на очень низком уровне: для каждого вью придется создавать отдельный класс со своим списком состояний и переходов, которые обрабатываются методом с кучей if-ов.
Это не идёт ни в какое сравнение с ООП реализацией из той библиотеки, про которую писал я. Самый огромный плюс этой библиотеки в том, что переходы описываются очень наглядно и в одном месте:
Если в результате изменений в проекте надо изменить порядок экранов, добавить экран, то достаточно в вышеприведенном описании подправить несколько строк. Ну, и если нужен новый экран, то просто отнаследоваться от базового класса, который имеет слушатели к стейт-машине.
Но данный ViewState может быть уместным в рамках данного вопроса, как обработчик состояния одной единственной активити или фрагмента. Но опять же отсутствие ООП в описании состояний может породить ужасный список условий, если мы будем иметь более-менее сложное поведение.
Поясню немного код, который вставил: если мы находимся в состоянии SHOWING_WELCOME, то при поступлении события cardPresent надо перейти в состояние WAITING_FOR_PIN, если пришло событие cancel, то переходим в состояние RETURNING_CARD. Если мы в состоянии WAITING_FOR_PIN, то при поступлении события pinProvided переходим к .... . Если мы в состоянии RETURNING_CARD и пришло событие cardExtracted, то возвращаемся к SHOWING_WELCOME.
Если надо иметь доступ к данным, и при этом быть независимым от жизненных циклов, можно попробовать дефолтное решение от Google: Android Architecture Components. Довольно просто использовать. Презентация с русскими субтитрами: https://youtu.be/Dl4DiQRxBi4