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

Какие знаешь способы сохранения состояния экрана при смене конфигурации устройства?

2.0 Middle🔥 161 комментариев
#UI и вёрстка#Архитектура и паттерны#Жизненный цикл и навигация

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

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

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

Способы сохранения состояния экрана при смене конфигурации устройства

Смена конфигурации устройства (например, поворот экрана, изменение языка, подключение внешней клавиатуры) приводит к уничтожению текущего Activity и созданию нового с новыми ресурсами. Это вызывает необходимость сохранения и восстановления состояния UI. Вот основные подходы:

1. Использование onSaveInstanceState() и onRestoreInstanceState()

Это базовый механизм, предоставляемый Android SDK. Activity автоматически вызывает onSaveInstanceState() перед уничтожением, позволяя сохранить данные в Bundle.

class MainActivity : AppCompatActivity() {
    private var counter = 0

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putInt("COUNTER_KEY", counter)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        counter = savedInstanceState.getInt("COUNTER_KEY")
    }
}

Ограничения: Bundle имеет ограниченный размер (~1MB), не предназначен для сложных объектов (лучше сериализовать их). Данные сохраняются только для временного восстановления после смены конфигурации, но не для полного уничтожения приложения.

2. ViewModel в сочетании с архитектурными компонентами Jetpack

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

class MyViewModel : ViewModel() {
    private val _counter = MutableLiveData<Int>(0)
    val counter: LiveData<Int> = _counter

    fun increment() {
        _counter.value = _counter.value?.plus(1)
    }
}

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

        viewModel.counter.observe(this, Observer { value ->
            // Обновляем UI, например, текст в TextView
            textView.text = value.toString()
        })
    }
}

Преимущества: ViewModel сохраняет данные даже при многократных поворотах экрана. Он легко интегрируется с LiveData для реактивного UI.

3. Персистентное сохранение в SharedPreferences, Room или файлы

Для данных, которые должны сохраняться между сессиями приложения (не только при смене конфигурации), используют персистентные хранилища.

  • SharedPreferences: Для простых ключ-значение данных (настройки, мелкие состояния).
    val prefs = getSharedPreferences("app_prefs", MODE_PRIVATE)
    prefs.edit().putInt("counter", counter).apply()
    
  • Room Database: Для сложных структурированных данных (список пользователей, история операций).
  • Файлы на внутреннем/внешнем хранилище: Для больших бинарных данных (изображения, документы).

Эти данные затем загружаются в ViewModel или напрямую в Activity при создании.

4. Отключение пересоздания Activity для конкретной конфигурации

В редких случаях можно запретить системе пересоздавать Activity при изменении определенной конфигурации (например, только для поворота экрана). Это делается через атрибут configChanges в манифесте.

<activity android:name=".MainActivity"
          android:configChanges="orientation|screenSize">
</activity>

После этого Activity не будет уничтожено при повороте, а вместо этого вызовится onConfigurationChanged().

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    // Адаптировать UI к новой конфигурации здесь
}

Важно: Этот подход требует самостоятельной обработки всех изменений конфигурации (например, перерасчета layouts для нового ориентации). Он не рекомендуется для большинства случаев, так как нарушает стандартный жизненный цикл и может привести к сложным багам.

5. Сохранение состояния в Fragment через setRetainInstance(true) (устаревший)

В Fragment можно было использовать метод setRetainInstance(true), который сохранял его instance при смене конфигурации. Однако этот подход сейчас считается устаревшим. Он приводил к сложностям с жизненным циклом и потере контекста. Jetpack ViewModel является его прямой и более правильной заменой.

Рекомендуемая современная практика

Для большинства современных приложений оптимальной стратегией является комбинация:

  1. ViewModel — как основной хранитель состояния UI данных во время смены конфигурации.
  2. onSaveInstanceState() — для сохранения минимально необходимой информации для восстановления после полного уничтожения процесса (например, ID текущего элемента, чтобы загрузить его данные из базы при повторном создании).
  3. Персистентное хранилище (Room, SharedPreferences) — для долговременного сохранения ключевых данных пользователя.
// Комбинированный пример: ViewModel + onSaveInstanceState для ID
class DetailViewModel : ViewModel() {
    var itemDetail: LiveData<Item>? = null
    fun loadItem(itemId: String) { /* загрузка из Room */ }
}

class DetailActivity : AppCompatActivity() {
    private lateinit var viewModel: DetailViewModel
    private var itemId: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        itemId = savedInstanceState?.getString("ITEM_ID") ?: intent.getStringExtra("ITEM_ID")
        viewModel = ViewModelProvider(this).get(DetailViewModel::class.java)

        itemId?.let { viewModel.loadItem(it) }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putString("ITEM_ID", itemId)
    }
}

Таким образом, ViewModel защищает данные от поворота экрана, а Bundle помогает восстановить контекст (например, переданный ID) в крайних случаях. Это обеспечивает robust и user-friendly опыт.