Как стать автором
Обновить

Комментарии 15

setRetainInstance(true) не избавляет вас от обязанности сохранять состояние, так как на не помогает в случаях когда Activity была уничтожена и затем восстановлена. Я вообще советую никогда эту опцию не использовать. Разве только в редких случаях, когда восстановление View крайне дорогая операция.
Интересная проблема выискивается после того, как мы откроем другую активити и вернемся назад, а все введенные данные останутся, ибо при открытии и возврате не вызывается метод onCreate.
Не вижу ни какой проблемы. При возвращении в предыдущую Activity она, в большинстве случаев, должна сохранять свое состояние.
Тут дело в том, что даже введенный текст в EditText сохранится, если оставить так как есть.
Если что, в EditText текст сохраняется и без чьей либо помощи. Это уже по умолчанию зашито в самой Activity.

Большинство стандартные компоненты сохраняют свое состояние, если у них есть уникальный ID

Вы пишете, что при убийстве из-за ннхватки памяти нам не нужно сохранять данные.


имхо, правильное поведение, когда пользовиель не знает, что происходило с процессом пока он был в фоне и все его данные сохраняются если он явно не пожелал обратного (например, закрыв активити)

Сейчас пересмотрел код еще раз и заметил, что при наследовании от BaseViewModel вы не определяете новый CREATOR. В таком случае, получается, у вас из Parselable должен восстановиться объект BaseViewModel а не ViewModel. Да и не видно работы с Parcel в самой ViewModel. Это так задумано или ошибка по недосмотру?
Вот тут-то и скрыта самая сильная магия. Так и задумано, это не ошибка. Я, честно скажу, я не знаю почему, но даже в таком виде ViewModel сохраняется полностью. Я пробовал записывать туда массивы, другие объекты, простые типы, все сохраняется даже при пустом CREATOR. Попробуйте запустить этот код и убедитесь. Кто может объяснить почему такое происходит?

Осмелюсь предположить, что на самом деле ничего-то толком и не сохраняется, сохраняется только ссылка на ViewModel, которая потом восстанавливается. В javadoc-ах к Parcel упоминается интересная особенность:


An unusual feature of Parcel is the ability to read and write active
  • objects. For these objects the actual contents of the object is not
  • written, rather a special token referencing the object is written. When
  • reading the object back from the Parcel, you do not get a new instance of
  • the object, but rather a handle that operates on the exact same object that
  • was originally written.

То есть, фактически, ничего не сериализуется, просто если не надо передавать данные меджу процессами (а Parcelable в том числе было сделано для IPC), в целях оптимизации производительности где-то магически держится ссылка на объект ViewModel, который в свою очередь держит ссылки на все объекты внутри себя. Это можно проверить, убив активити (например, свернув его и нажав в Android Studio на панели Android Monitor кнопку "Terminate Application"). Если данные не сериализовались, а просто где-то держалась ссылка на ViewModel, данные не восстановлятся.
Это скорее предположение + немного смутных воспоминаний из своего опыта. Стоит проверить.

Да, действительно. Если включить Don't keep activities, то все летит в нехорошие места. Получается на самом деле нужно реализовывать Creator в классах наследниках. А в таком случае реализовывать Parceble в базовом классе вообще нету смысла.
Никакой тут магии нет. Вы просто эксплуатируете незадокументированное поведение Android API: cистема не сериализует вашу ViewModel при поворотах, а сохраняет во временой переменой. Вся магия начнется когда вы пойдете в настройки, включите Don't keep activities, перейдете на новую Activity, вернетесь назад:

Caused by: java.lang.ClassCastException: com.quinque.aether.reactivemvvm.base.BaseViewModel cannot be cast to com.quinque.aether.reactivemvvm.ViewModel

На самом деле ваше решение ничем не отличается от того чтоб создать статическое поле и туда записывать вашу ViewModel на момент пересоздания Activity. Вся прелесть Parcelable что модель должна выживать даже когда Android убивает вашу Activity.
Спасибо на указание ошибки, поправил код и статью. Стало не так все красиво и уже не без боли, но основная идея все еще видна. Еще буду думать над более изящным решением.

Думаю вам стоит посмотреть в сторону Loaders. Насколько мне известно это сейчас стандратное решение для данной проблемы.

Да нет, что вы, лоадеры помогают в некоторой степени бороться с поворотами, но от нехватки ресурсов все так же не спасают. Я в последнее время решил не бороться с платформой, а подружиться с ней. Фактически, Теперь у меня при убийстве презентеры (у меня MVP, а не MVVM на текущем проекте, но сути это не меняет) умирают и пересоздаются вместе с активити/фрагментами. Все, что надо, сохраняю в Bundle. Как бонус, теперь не нужно проверять, прикреплена ли View к презентеру, потому что они не существуют раздельно, плюс поддерживается восстановление не только при смене ориентации, но и при смерти от нехватки ресурсов.

Скрытый текст
За этим должны следить все приложения, но очень многие об этом либо забывают, либо не заморачиваются. Очень разозлило, когда писал комментарий в Redmine, потом пошел что-то погуглить, и по возвращению увидел, что мой комментарий исчез.


Теоретически может возникнуть ситуация, когда при пересоздании активити восстанавливается слишком много всего, тогда можно отключить пересоздание этой activity и обрабатывать ситуацию вручную (при восстановлении после нехватки памяти время не так критично, потому что пользователь покидал экран/приложение и не ждет моментального отклика) или сделать retain fragment, но на практике мне пока не приходилось такое делать даже на довольно нагруженных экранах.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Публикации

Истории