Как устроен жизненный цикл ViewModel в Android и когда сохраняется инстанс при смене конфигурации
Что проверяют
Понимает ли кандидат механизм, а не магию: что именно хранит ViewModel при пересоздании активити и когда вызывается onCleared. Почти всегда проверяют и границу применимости, то есть поведение при смерти процесса.
Как отвечать
ViewModel не наблюдает жизненный цикл и не должна о нём знать. Её хранит ViewModelStore, владелец стора это ViewModelStoreOwner: ComponentActivity, Fragment, NavBackStackEntry. При запросе через by viewModels() фабрика создаёт объект один раз и кладёт в стор по ключу, следующий запрос отдаёт тот же инстанс.
Смена конфигурации: активити действительно уничтожается и создаётся заново, но ViewModelStore переезжает в новый экземпляр через механизм сохранения нонконфигурационного состояния (onRetainNonConfigurationInstance). Поэтому после поворота viewModels() возвращает прежний объект вместе с уже загруженными данными, и повторный запрос в сеть не нужен.
Когда инстанс умирает: при окончательном уничтожении владельца, то есть когда активити реально завершается (isFinishing), а не пересоздаётся. Тогда вызывается onCleared(), и в нём же автоматически отменяется viewModelScope. У фрагмента граница своя: ушедший в back stack фрагмент сохраняет свою ViewModel, а by activityViewModels() даёт общий на активити инстанс для обмена данными между фрагментами.
Главная граница, которую и спрашивают: смерть процесса ViewModel не переживает. Система выгрузила приложение из памяти в фоне, пользователь вернулся, активити восстанавливается из Bundle, а ViewModel создаётся заново и пустая. Для состояния, которое должно пережить и это, есть SavedStateHandle, он сохраняется в тот же бандл.
class SearchViewModel(private val state: SavedStateHandle) : ViewModel() {
val query: StateFlow<String> = state.getStateFlow("query", "")
fun onQueryChange(value: String) { state["query"] = value }
}Разделение простое: в SavedStateHandle кладут маленькое состояние экрана (введённый текст, выбранный таб, id открытой записи), а тяжёлые данные после восстановления перезапрашивают из репозитория.
Практические следствия:
- ссылка на
Activity,Viewили ихContextвнутриViewModelэто утечка, объект живёт дольше них. Если нужен контекст, берутAndroidViewModelс application-контекстом; - разовые события (навигация, снекбар) через
StateFlowпродублируются после поворота, поэтому их оформляют какChannel/SharedFlowлибо как флаг в состоянии со сбросом после потребления; - в Compose
ViewModelпо умолчанию привязана кNavBackStackEntryмаршрута, и переход назад её очищает.
Где спотыкаются
Отвечают «ViewModel привязана к LifecycleOwner и потому переживает поворот»: зависимость обратная, объект живёт в сторе, который переезжает мимо жизненного цикла. Второе: считают ViewModel заменой SavedStateHandle и теряют состояние после выгрузки процесса.