Найти в Дзене

Как ViewModel переживает смену конфигурации?

Помню, как однажды я провела целую вечность, заполняя форму и вводя тонну информации и адрес, чтобы сделать заказ. Но потом я случайно повернула телефон, и всё пропало. После этого я больше никогда не заходила в это приложение. Сейчас этой компании не существует. А вот если бы они задумались о смене конфигурации и сделали бы всё правильно с самого начала, может быть, они были бы популярными до сих пор. :) Когда происходит смена конфигурации (например, поворот экрана), система уничтожает активити и создает новую. Из-за этого у разработчиков всегда много проблем, потому что данные теряются и надо как-то их сохранять/восстанавливать. ViewModel очень сильно выручает нас тут. Она спроектирована таким образом, что может переживать смену конфигурации. Краткое описание как это работает: Это всё означает, что ViewModel сохраняется в памяти, даже когда активити уничтожается и создается заново, что позволяет сохранять данные. (Важно!) Мы не касаемся сценария, когда активити уничтожается окончат
Оглавление

Помню, как однажды я провела целую вечность, заполняя форму и вводя тонну информации и адрес, чтобы сделать заказ. Но потом я случайно повернула телефон, и всё пропало. После этого я больше никогда не заходила в это приложение. Сейчас этой компании не существует. А вот если бы они задумались о смене конфигурации и сделали бы всё правильно с самого начала, может быть, они были бы популярными до сих пор. :)

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

ViewModel очень сильно выручает нас тут. Она спроектирована таким образом, что может переживать смену конфигурации. Краткое описание как это работает:

  1. ViewModelStore: у каждой активити есть ViewModelStore (https://github.com/androidx/androidx/blob/androidx-main/lifecycle/lifecycle-viewmodel/src/androidMain/kotlin/androidx/lifecycle/ViewModelStore.kt). Подробнее про это я писала здесь: https://dzen.ru/a/ZUZ6NobTliU1wfgd. Когда активити уничтожается при смене конфигурации, ее ViewModelStore не уничтожается вместе с ней. Вместо этого ViewModelStore сохраняется через механизм, который называется "retaining".
  2. Retaining: когда активити уничтожается из-за смены конфигурации, система знает об этом и удерживает ViewModelStore (звучит как магия, правда?). После того как новый экземпляр активити создается, он получает тот же ViewModelStore, что и предыдущая активити.
  3. ViewModelProvider: когда мы создаем ViewModel через ViewModelProvider, он проверяет, существует ли уже ViewModel в ViewModelStore. Если да, то он возвращает существующий экземпляр вместо создания нового.

Это всё означает, что ViewModel сохраняется в памяти, даже когда активити уничтожается и создается заново, что позволяет сохранять данные.

(Важно!) Мы не касаемся сценария, когда активити уничтожается окончательно, например, когда пользователь нажимает кнопку "Назад". В таких случаях ViewModel уничтожается окончательно и будет вызван метод onCleared().

Звучит всё просто. Но что ещё за retaining? Давайте разберемся.

Перед уничтожением активити из-за смены конфигурации вызывается метод onRetainNonConfigurationInstance (https://developer.android.com/reference/android/app/Activity#onRetainNonConfigurationInstance()). Этот метод вызывается автоматически системой внутри жизненного цикла активити и помогает сохранять данные. Разработчики его не трогают и ничего там не пишут. За нас уже всё сделали.

Система использует возвращаемое значение этого метода и сохраняет его на время пока активити пересоздаётся. Затем, при создании новой активити, это сохраненное значение можно получить с помощью метода getLastNonConfigurationInstance (https://developer.android.com/reference/android/app/Activity#getLastNonConfigurationInstance()).

Вот и вся магия. Теперь, когда ViewModelProvider запросит ViewModelStore, то он получит сохраненный экземпляр и вернет соответствующие ViewModel, которые там хранились.

Подробнее про методы:

onRetainNonConfigurationInstance:

  • Это метод в активити, который используется для сохранения данных при смене конфигурации, например, при повороте экрана.
  • Метод вызывается перед уничтожением активити и её последующим созданием из-за смены конфигурации.
  • Метод возвращает объект, представляющий данные, которые мы хотим сохранить. Это может быть любой объект или структура данных по нашему усмотрению. Что хотим, то и сохраняем (котика, уши, лапы, хвост, строку).
  • Возвращенный объект будет передан новому экземпляру активити после её пересоздания.

Пример реализации из документации:

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/ComponentActivity.kt;l=354?q=onRetainNonConfigurationInstance
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/ComponentActivity.kt;l=354?q=onRetainNonConfigurationInstance

Тут видно, что в этом методе сохраняется ViewModelStore, который как раз хранит все наши ViewModel.

getLastNonConfigurationInstance:

  • Используется для получения объекта, который был возвращен в методе onRetainNonConfigurationInstance.
  • Его можно вызвать самостоятельно. Но обычно внутри метода onCreate пересозданной активити.
  • Именно этот метод используется для ViewModel, чтобы получить сохраненный ViewModelStore.

Пример реализации из документации:

https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/app/Activity.java;l=3237?q=getLastNonConfigurationInstance&sq=
https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/app/Activity.java;l=3237?q=getLastNonConfigurationInstance&sq=

Тут метод совсем простой. Возвращаем сохраненную конфигурацию если она есть. Если нет, то возвращаем null.

-3

Дубль статей в телеграмме — https://t.me/android_junior

Мои заметки в телеграмме — https://t.me/android_junior_notes

P.S. сделано с помощью документации, ChatGPT и Midjourney. :)