Ну тут они советуют использовать родные ретейн фрагменты, которые появились начиная с HONEYCOMB (версия 3.0-3.2). Но если посмотреть исходники внимательно, то можно увидеть, что родные и саппорт фрагменты хранятся и передаются через NonConfigurationInstances, аналогично и лоадеры.
А вот второе замечание интересное, что onRetainNonConfigurationInstance может в каки-то случаях не вызваться. Но что-то мне подсказывает на практике это никогда не случается. Иначе на ретейн фрагменты тоже нельзя полагаться.
Добавлю что LiveData внутри завязана на Handler, и как прогонять unit-тесты?
Самое неприятное, что теперь решение ViewModel + LiveData считается де-факто стандартом. Большинство доверяют только Гуглу и смотреть в сторону намного лучших MV*-решений даже не хотят. Тот же PM или MVVM несложно реализовать на RxJava.
Удивительно, что кто-то еще не пишет приложения в Single-Activity. В любой момент может потребоваться поместить экран в NavigationDrawer, ViewPager или BottomBar. В этом случае без кучи фрагментов в одной Activity не обойтись.
Рестарт процесса вещь неприятная, но не всегда требуется при этом восстанавливать View в то же самое состояние. Так как данные за время отсутствия пользователя в приложении могли устареть. Все зависит от конкретного приложения и в каждом случае нужно то или иное решение:
1) Точно восстановится бэкстек из активити и фрагментов. При желании этот момент можно отследить и очистить бекстек, если приложение стартовало с восстановлением.
2) Самые важные параметры экранов (параметры запуска) мы стараемся передавать через Intent или аргументы фрагмента, например id сущностей, которые нужно отобразить. PresentationModel получает их в конструкторе, так как View провайдит ее.
3) Не все состояния нужно восстанавливать. Например прогресс загрузки не нужно восстанавливать, так как с убийством процесса все асинхронные запросы (в том числе и в сеть) тоже завершатся.
4) Есть данные, которые быстро устаревают, например какие-нибудь статусы заказа. Лучше будет их заново запросить с сервера.
5) Некоторые данные следует восстанавливать даже после принудительного завершения приложения. В этом случае никакие bundle нам не помогут. Например это может быть корзина с продуктами. Такие данные во время работы приложения нужно сохранять на диск (в бд или файл).
6) Хорошо кешировать данные, которые не сильно теряют актуальность за относительно продолжительное время.
7) Можно запустить сервис, чтобы повысить приоритет приложения в фоне. Тем самым снизить вероятность убийства процесса системой.
8) В конце концов в PresentationModel можно пробрасывать вызовы сохранения/восстановления состояния из bundle. Но этот вариант не подходит для персистентных данных (пункт 5 и 6).
Основная идея паттерна PM заключается в том, что стейт хранится в PresentationModel. View не нужно об этом беспокоиться и не нужно складывать стейт в Bundle. Главное реализовать хранение PresentationModel во время поворота.
По поводу навигации, нужно складывать команды в буфер, и воспроизводить их когда навигатор (активити) будет готов. Посмотрите как это сделано в Cicerone.
Не все пишут на Котлине, можно делать интерфейс-заглушку, но ее тоже придется генерировать. Но проблема не в этом. Так как вьюха может быт отсоединена, то приходится сохранять стейт в презентере в виде флагов, чтобы потом его воспроизвести при атаче вью.
Ретейн фрагмент только для семпла, так то для прода они не годятся. Как вы уже заметили в бекстеке такие фрагменты нельзя использовать и есть баги с чайлд-фрагментами. Я в своих приложениях использую Conductor — это такие "правильные" фрагменты, которые не умирают в бекстеке и при поворотах. А насчет памяти тут все в порядке, на onDestroyView мы отписываемся от PresentationModel.
Все ошибки можно разделить на два типа:
1) Ошибки, которые нужно показать один раз, например AlertDialog или Toast. В этом случае ошибка будет эвентом, ее сохранять не нужно. Для этого подойдет обычный PublishRelay. Но будет проблема, если ошибка прилетит в тот момент, когда вьюха отсоединена от PresentationModel. В этом случае мы потеряем этот эвент. Как решать эту проблему я расскажу в следующей статье.
2) Ошибки, которые нужно показывать как заглушку в разметке, например с кнопкой "Retry", такой вариант нужно считать стейтом. Для этого нужно использовать BehaviorRelay.
Например, если нужно открыть доступ к свойству или методу не всем подряд (public), а только классам, которые реализуют некоторый контракт.
А какой профит дружественных классов в С++?)
А вот второе замечание интересное, что onRetainNonConfigurationInstance может в каки-то случаях не вызваться. Но что-то мне подсказывает на практике это никогда не случается. Иначе на ретейн фрагменты тоже нельзя полагаться.
getLastNonConfigurationInstance доступен с первой версии API
Для фрагментов в бэкстеке ваш способ не пройдет, у них дестроится вью при реплейсе. И придется восстанавливать стейт.
Добавлю что LiveData внутри завязана на Handler, и как прогонять unit-тесты?
Самое неприятное, что теперь решение ViewModel + LiveData считается де-факто стандартом. Большинство доверяют только Гуглу и смотреть в сторону намного лучших MV*-решений даже не хотят. Тот же PM или MVVM несложно реализовать на RxJava.
Conductor вам в помощь
Удивительно, что кто-то еще не пишет приложения в Single-Activity. В любой момент может потребоваться поместить экран в NavigationDrawer, ViewPager или BottomBar. В этом случае без кучи фрагментов в одной Activity не обойтись.
В RxJava для этих целей есть Connectable Observable и Subject
Что насчет combineLatest ?
все картинки сделаны в Sketch
Можете привести пример из жизни, когда стандартными стратегиями не обойтись?
Их много — значит они
Точно так же как и в Moxy ;)
Рестарт процесса вещь неприятная, но не всегда требуется при этом восстанавливать View в то же самое состояние. Так как данные за время отсутствия пользователя в приложении могли устареть. Все зависит от конкретного приложения и в каждом случае нужно то или иное решение:
1) Точно восстановится бэкстек из активити и фрагментов. При желании этот момент можно отследить и очистить бекстек, если приложение стартовало с восстановлением.
2) Самые важные параметры экранов (параметры запуска) мы стараемся передавать через Intent или аргументы фрагмента, например id сущностей, которые нужно отобразить. PresentationModel получает их в конструкторе, так как View провайдит ее.
3) Не все состояния нужно восстанавливать. Например прогресс загрузки не нужно восстанавливать, так как с убийством процесса все асинхронные запросы (в том числе и в сеть) тоже завершатся.
4) Есть данные, которые быстро устаревают, например какие-нибудь статусы заказа. Лучше будет их заново запросить с сервера.
5) Некоторые данные следует восстанавливать даже после принудительного завершения приложения. В этом случае никакие bundle нам не помогут. Например это может быть корзина с продуктами. Такие данные во время работы приложения нужно сохранять на диск (в бд или файл).
6) Хорошо кешировать данные, которые не сильно теряют актуальность за относительно продолжительное время.
7) Можно запустить сервис, чтобы повысить приоритет приложения в фоне. Тем самым снизить вероятность убийства процесса системой.
8) В конце концов в PresentationModel можно пробрасывать вызовы сохранения/восстановления состояния из bundle. Но этот вариант не подходит для персистентных данных (пункт 5 и 6).
.
Основная идея паттерна PM заключается в том, что стейт хранится в PresentationModel. View не нужно об этом беспокоиться и не нужно складывать стейт в Bundle. Главное реализовать хранение PresentationModel во время поворота.
По поводу навигации, нужно складывать команды в буфер, и воспроизводить их когда навигатор (активити) будет готов. Посмотрите как это сделано в Cicerone.
Все ошибки можно разделить на два типа:
1) Ошибки, которые нужно показать один раз, например AlertDialog или Toast. В этом случае ошибка будет эвентом, ее сохранять не нужно. Для этого подойдет обычный PublishRelay. Но будет проблема, если ошибка прилетит в тот момент, когда вьюха отсоединена от PresentationModel. В этом случае мы потеряем этот эвент. Как решать эту проблему я расскажу в следующей статье.
2) Ошибки, которые нужно показывать как заглушку в разметке, например с кнопкой "Retry", такой вариант нужно считать стейтом. Для этого нужно использовать BehaviorRelay.
Залил примерчик на гитхаб https://github.com/dmdevgo/RxPM-Demo