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

Как ViewModel может пережить поворот экрана

1.8 Middle🔥 231 комментариев
#Архитектура и паттерны#Жизненный цикл и навигация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сохранение состояния с помощью ViewModel

ViewModel — это компонент архитектуры Android, специально разработанный для хранения и управления UI-данными, связанными с жизненным циклом. Его ключевая особенность — способность переживать изменения конфигурации, такие как поворот экрана. Это достигается не за счет сохранения самого объекта, а через механизм повторного использования, управляемый системой.

Как это работает: жизненный цикл и хранилище

  1. Создание и привязка к жизненному циклу: ViewModel создается с помощью ViewModelProvider, который привязывает экземпляр ViewModel к конкретному LifecycleOwner (например, Fragment или Activity). Провайдер хранит ViewModel в специальном хранилище.

  2. Использование ViewModelStore: За кулисами существует объект ViewModelStore, который содержит коллекцию (например, HashMap) ViewModel'ей. Каждому LifecycleOwner (Activity или Fragment) соответствует свой ViewModelStore.

  3. Ключевая роль ComponentActivity: При повороте экрана Activity уничтожается и создается заново. Однако, если ваш Activity наследуется от ComponentActivity (или AppCompatActivity, которая его расширяет), его ViewModelStore не уничтожается системой при изменении конфигурации. Вместо этого хранилище передается новому экземпляру Activity.

  4. Повторное использование: Когда новый экземпляр Activity запрашивает ViewModel того же типа и с тем же ключом (обычно это класс активити или фрагмента), ViewModelProvider сначала проверяет ViewModelStore старого экземпляра. Если там есть подходящая ViewModel, она возвращается новому владельцу жизненного цикла. Таким образом, объект ViewModel не уничтожается и не пересоздается, а данные в нем сохраняются.

// Пример получения ViewModel в Activity. При повороте экрана
// будет возвращен тот же экземпляр `MyViewModel`.
class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // ViewModelProvider использует бэкенд (ViewModelStore) для
        // поиска существующего или создания нового экземпляра.
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

        // Начинаем наблюдать за LiveData. При повороте экрана подписка
        // будет автоматически пересоздана, но сама LiveData и ее значение
        // сохранятся в ViewModel.
        viewModel.data.observe(this) { data ->
            // Обновляем UI с сохраненными данными
        }
    }
}

Преимущества подхода ViewModel

  • Автоматическое управление: Вам не нужно вручную обрабатывать onSaveInstanceState() для простых данных UI (хотя для навигации или критичных данных это все еще нужно).
  • Эффективность: Сохраняются тяжелые объекты (списки, сетевые ответы), которые нецелесообразно сериализовать в Bundle.
  • Разделение ответственности: UI-контроллер (Activity/Fragment) отвечает только за отображение данных и обработку ввода, а бизнес-логика и данные живут в ViewModel.

Важные нюансы и ограничения

  • ViewModel НЕ переживает полное уничтожение процесса. Если система убьет приложение из-за нехватки памяти, все ViewModel'и будут уничтожены. Для восстановления состояния в этом случае используйте onSaveInstanceState() в сочетании с ViewModel или SavedStateHandle.
  • SavedStateHandle — это современный способ сохранять в ViewModel минимально необходимые данные (ID, простые типы) для восстановления после смерти процесса. Он интегрирован в ViewModel.
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    // Сохраняем и читаем данные через SavedStateHandle.
    // Они переживут и поворот экрана, и смерть процесса.
    var userId: String
        get() = savedStateHandle["userId"] ?: ""
        set(value) = savedStateHandle.set("userId", value)

    // Остальные "тяжелые" данные, которые не нужны для
    // немедленного восстановления, можно хранить просто как свойства.
    val userData: LiveData<User> = ...
}
  • Не храните контексты в ViewModel! ViewModel живет дольше, чем UI-контроллер. Хранение ссылки на Activity, View или Context, привязанный к Activity, приведет к утечке памяти. Если нужен Context, используйте AndroidViewModel, который предоставляет Application Context.

Таким образом, ViewModel переживает поворот экрана благодаря тому, что система сохраняет его хранилище (ViewModelStore) и передает его новому экземпляру UI-контроллера, обеспечивая целостность данных и улучшая пользовательский опыт.