Как сохранить состояние экрана при смене конфигурации устройства с помощью ViewModel
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Сохранение состояния экрана при смене конфигурации с помощью ViewModel
При смене конфигурации устройства (поворот экрана, изменение языка, подключение клавиатуры) система Android по умолчанию уничтожает и заново создает Activity/Fragment. Это приводит к потере данных, которые хранились в UI-компонентах. ViewModel — это компонент архитектуры Android, предназначенный для хранения и управления UI-данными, связанными с жизненным циклом, способный пережить такие изменения конфигурации.
Основные принципы работы ViewModel
ViewModel сохраняется при изменении конфигурации благодаря своей связи с LifecycleOwner (обычно Activity или Fragment) через ViewModelProvider. Когда владелец уничтожается и воссоздается из-за смены конфигурации, ViewModel остается в памяти, и новый экземпляр владельца получает ссылку на тот же объект ViewModel. Это происходит потому, что ViewModel хранится в ViewModelStore, который ассоциирован с контекстом приложения, а не с конкретным экземпляром Activity.
Реализация с использованием ViewModel
Рассмотрим практический пример. Допустим, у нас есть счетчик, значение которого не должно сбрасываться при повороте экрана.
1. Создание класса ViewModel:
import androidx.lifecycle.ViewModel
class CounterViewModel : ViewModel() {
private var _counter = 0
val counter: Int get() = _counter
fun increment() {
_counter++
}
}
2. Использование ViewModel во Fragment (или Activity):
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
import androidx.fragment.app.Fragment
class MyFragment : Fragment() {
private lateinit var viewModel: CounterViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Инициализация ViewModel
viewModel = ViewModelProvider(this).get(CounterViewModel::class.java)
// Наблюдение за изменениями счетчика
viewModel.counter.observe(viewLifecycleOwner) { count ->
textView.text = "Count: $count"
}
// Обработка клика для увеличения счетчика
button.setOnClickListener {
viewModel.increment()
}
}
}
Ключевые преимущества подхода
- Автоматическое сохранение состояния: ViewModel переживает смену конфигурации без дополнительного кода.
- Разделение ответственности: Логика данных отделена от UI-логики, что соответствует принципам чистой архитектуры.
- Предотвращение утечек памяти: ViewModel автоматически очищается, когда связанный с ней LifecycleOwner полностью уничтожается (не из-за смены конфигурации).
- Совместимость с LiveData/StateFlow: ViewModel идеально сочетается с наблюдаемыми компонентами для реактивного UI.
Важные ограничения и дополнения
-
ViewModel НЕ переживает завершение процесса: Для сохранения данных между полным завершением приложения необходимо использовать SavedStateHandle или постоянное хранилище.
-
Использование SavedStateHandle:
class CounterViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
companion object {
private const val COUNTER_KEY = "counter_key"
}
val counter = savedStateHandle.getLiveData<Int>(COUNTER_KEY, 0)
fun increment() {
val current = counter.value ?: 0
savedStateHandle.set(COUNTER_KEY, current + 1)
}
}
- Не храните ссылки на View/Context: ViewModel переживает View, поэтому хранение ссылок на контекст Activity может вызвать утечки памяти. Используйте Application Context, если необходим контекст.
Рекомендуемая архитектура с ViewModel
Fragment/Activity (UI Layer)
↓ (наблюдает)
ViewModel (Business Logic Layer)
↓ (использует)
Repository (Data Layer)
↓
Data Sources (Local/Remote)
ViewModel становится центральным компонентом для управления состоянием UI, обеспечивая стабильность данных при любых изменениях конфигурации устройства, при этом соблюдая принципы чистого разделения кода и эффективного управления памятью.