← Назад к вопросам

Как 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

На собеседовании

Важные моменты для объяснения:

  1. ViewModelStore — контейнер, который живёт дольше Activity
  2. ComponentActivity — управляет ViewModelStore
  3. viewModels() — delegation, которая использует ViewModelStore
  4. Конфигурационные изменения — поворот экрана (Activity пересоздаётся, ViewModel нет)
  5. onCleared() — вызывается когда Activity действительно уничтожена
  6. viewModelScope — автоматически отменяется при onCleared()

Это элегантное решение, которое позволяет сохранять состояние без ручного сохранения в Bundle или SharedPreferences.

Как Viewmodel остается жить после смерти Activity | PrepBro