← Назад к вопросам
Как Viewmodel остается жить после смерти Activity
2.0 Middle🔥 201 комментариев
#Архитектура и паттерны#Жизненный цикл и навигация
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как ViewModel остаётся в живых после смерти Activity
Это один из ключевых паттернов в Android архитектуре и обычный вопрос на собеседовании. ViewModel специально разработана для того, чтобы пережить конфигурационные изменения (поворот экрана) и пересоздание Activity.
Проблема: потеря состояния при пересоздании Activity
// ❌ БЕЗ ViewModel — при повороте экрана теряется состояние
class MainActivity : AppCompatActivity() {
private var userData: User? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// При повороте экрана эта переменная будет null!
if (userData == null) {
userData = fetchUserFromNetwork() // Ещё один запрос
}
}
}
ViewModel спасает положение
ViewModel хранится в отдельном контейнере (ViewModelStore), который НЕ пересоздаётся при конфигурационных изменениях:
class UserViewModel : ViewModel() {
// Этот объект живёт дольше Activity
private val _userData = MutableLiveData<User>()
val userData: LiveData<User> = _userData
init {
fetchUserData()
}
fun fetchUserData() {
viewModelScope.launch(Dispatchers.IO) {
val user = api.getUser()
_userData.postValue(user)
}
}
}
class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Первый раз: создаётся ViewModel и загружаются данные
// При повороте: ViewModel переиспользуется, данные НЕ загружаются заново
viewModel.userData.observe(this) { user ->
updateUI(user)
}
}
}
Механизм: ViewModelStore и ComponentActivity
Вот что происходит под капотом:
// 1. ComponentActivity (базовый класс AppCompatActivity) имеет ViewModelStore
abstract class ComponentActivity : Activity, LifecycleOwner {
private var viewModelStore: ViewModelStore? = null
fun getViewModelStore(): ViewModelStore {
if (viewModelStore == null) {
viewModelStore = ViewModelStore() // Создаём только один раз
}
return viewModelStore
}
}
// 2. ViewModelStore хранит все ViewModel'и по ключу
class ViewModelStore {
private val store = mutableMapOf<String, ViewModel>()
fun put(key: String, viewModel: ViewModel) {
store[key] = viewModel
}
fun get(key: String): ViewModel? = store[key]
}
// 3. ViewModelProvider использует ViewModelStore для создания/восстановления
class ViewModelProvider(private val store: ViewModelStore) {
fun <T : ViewModel> get(key: String, factory: ViewModelFactory): T {
var viewModel = store.get(key)
if (viewModel == null) {
viewModel = factory.create() // Создаём только если не существует
store.put(key, viewModel)
}
return viewModel as T
}
}
Жизненный цикл при повороте экрана
первый запуск (portrait)
↓
Activity.onCreate() → ViewModel создаётся
↓
Activity.onDestroy() (конфигурационное изменение)
↓
[ViewModelStore ОСТАЁТСЯ В ПАМЯТИ]
↓
Activity.onCreate() (landscape) → ViewModel восстанавливается из хранилища
↓
Activity.onDestroy() (пользователь закрыл приложение)
↓
ViewModel.onCleared() → очистка ресурсов
Невозвратные сценарии (когда ViewModel умирает)
class UserViewModel : ViewModel() {
override fun onCleared() {
// Вызывается когда Activity ДЕЙСТВИТЕЛЬНО уничтожена
// (не при конфигурационном изменении)
println("ViewModel очищена")
// Отменяем корутины
viewModelScope.cancel()
// Закрываем ресурсы
disposable?.dispose()
}
}
// ViewModel умирает в этих случаях:
// 1. Пользователь закрывает Activity (back button)
// 2. Activity убивает система (нехватка памяти)
// 3. Фрагмент удаляется из контейнера
// 4. finish() или finishActivity()
Практический пример с Flow
class UserViewModel : ViewModel() {
private val _userFlow = MutableStateFlow<User?>(null)
val userFlow: StateFlow<User?> = _userFlow.asStateFlow()
init {
// Корутина живёт в viewModelScope (привязана к ViewModel)
viewModelScope.launch(Dispatchers.IO) {
while (true) {
try {
val user = api.getUser()
_userFlow.emit(user)
delay(30000) // Опрос каждые 30 секунд
} catch (e: Exception) {
logger.error("Failed to fetch user", e)
}
}
}
}
override fun onCleared() {
// При закрытии Activity — автоматически отменяется viewModelScope
// и цикл while останавливается
super.onCleared()
}
}
Почему это безопасно
NonConfigurationInstance — Android встроен в Activity:
abstract class Activity {
private val nonConfigurationInstances: NonConfigurationInstances? = null
// При конфигурационном изменении:
override fun onSaveInstanceState() {
// Сохраняется nonConfigurationInstances
}
// При пересоздании:
override fun onCreate(savedInstanceState: Bundle?) {
// Восстанавливается nonConfigurationInstances
}
}
ViewModelStore заранее в nonConfigurationInstances, поэтому:
- Не участвует в Bundle (не сериализуется)
- Живёт в памяти Java heap'а
- Недоступна в onSaveInstanceState() и restoration
На собеседовании
Важные моменты для объяснения:
- ViewModelStore — контейнер, который живёт дольше Activity
- ComponentActivity — управляет ViewModelStore
- viewModels() — delegation, которая использует ViewModelStore
- Конфигурационные изменения — поворот экрана (Activity пересоздаётся, ViewModel нет)
- onCleared() — вызывается когда Activity действительно уничтожена
- viewModelScope — автоматически отменяется при onCleared()
Это элегантное решение, которое позволяет сохранять состояние без ручного сохранения в Bundle или SharedPreferences.