Обрабатывать изменения конфигурации

Некоторые конфигурации устройства могут меняться во время работы приложения. К ним относятся, помимо прочего:

  • Размер дисплея приложения
  • Ориентация экрана
  • Размер и вес шрифта
  • Языковой стандарт
  • Темный режим и светлый режим
  • Наличие клавиатуры

Большинство этих изменений конфигурации происходят из-за взаимодействия с пользователем. Например, поворот или складывание устройства изменяет объем экранного пространства, доступного для вашего приложения. Аналогично, изменение настроек устройства, таких как размер шрифта, язык или предпочтительная тема, изменяет соответствующие значения в объекте Configuration .

Эти параметры обычно требуют достаточно больших изменений в пользовательском интерфейсе вашего приложения, чтобы платформа Android имела специальный механизм для их изменения. Этот механизм – Activity воссоздание .

Активный отдых

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

Поведение воссоздания помогает вашему приложению адаптироваться к новым конфигурациям путем автоматической перезагрузки приложения с альтернативными ресурсами, соответствующими новой конфигурации устройства.

Пример отдыха

Рассмотрим TextView , который отображает статический заголовок с помощью android:text="@string/title" , как определено в XML-файле макета. Когда представление создается, оно устанавливает текст ровно один раз в зависимости от текущего языка. Если язык меняется, система воссоздает действие. Следовательно, система также воссоздает представление и инициализирует его правильным значением на основе нового языка.

Воссоздание также очищает любое состояние, сохраненное в виде полей в Activity или в любом из содержащихся в нем Fragment , View или других объектах. Это связано с тем, что при воссоздании Activity создается совершенно новый экземпляр Activity и пользовательского интерфейса. Более того, старая Activity больше не видна и недействительна, поэтому все оставшиеся ссылки на нее или содержащиеся в ней объекты устарели. Они могут вызывать ошибки, утечки памяти и сбои.

Ожидания пользователей

Пользователь приложения ожидает, что состояние будет сохранено. Если пользователь заполняет форму и открывает другое приложение в многооконном режиме для получения информации, возврат к очищенной форме или к другому месту в приложении будет неприятным для пользователя. Как разработчик, вы должны обеспечить единообразный пользовательский интерфейс посредством изменения конфигурации и воссоздания действий.

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

  • Поворот устройства
  • Вход в многооконный режим
  • Изменение размера приложения в многооконном режиме или в окне произвольной формы.
  • Складывание складного устройства с несколькими дисплеями
  • Изменение темы системы, например темный режим вместо светлого.
  • Изменение размера шрифта
  • Изменение языка системы или приложения
  • Подключение или отключение аппаратной клавиатуры
  • Подключение или отключение док-станции

Существует три основных подхода, которые можно использовать для сохранения соответствующего состояния посредством воссоздания Activity . Что использовать, зависит от типа состояния, которое вы хотите сохранить:

  • Локальное постоянство для обработки смерти процесса для сложных или больших данных. Постоянное локальное хранилище включает базы данных или DataStore .
  • Сохраняемые объекты , такие как экземпляры ViewModel , для обработки состояния пользовательского интерфейса в памяти, когда пользователь активно использует приложение.
  • Сохраненное состояние экземпляра для обработки смерти процесса, инициированного системой, и сохранения переходного состояния, которое зависит от ввода пользователя или навигации.

Подробную информацию об API для каждого из них и о том, когда их использование уместно, см. в разделе Сохранение состояний пользовательского интерфейса .

Ограничить активный отдых

Вы можете запретить автоматическое воссоздание активности при определенных изменениях конфигурации. Воссоздание Activity приводит к воссозданию всего пользовательского интерфейса и любых объектов, производных от Activity . У вас могут быть веские причины избегать этого. Например, вашему приложению может не потребоваться обновление ресурсов во время определенного изменения конфигурации или у вас может быть ограничение производительности. В этом случае вы можете объявить, что ваша активность сама обрабатывает изменение конфигурации, и запретить системе перезапускать вашу активность.

Чтобы отключить воссоздание активности для определенных изменений конфигурации, добавьте тип конфигурации в android:configChanges в записи <activity> в файле AndroidManifest.xml . Возможные значения атрибута android:configChanges приведены в документации.

Следующий код манифеста отключает воссоздание Activity для MyActivity при изменении ориентации экрана и доступности клавиатуры:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

Некоторые изменения конфигурации всегда приводят к перезапуску активности. Вы не можете их отключить. Например, вы не можете отключить динамическое изменение цветов , представленное в Android 12L (уровень API 32).

Реагировать на изменения конфигурации в системе View

В системе View , когда происходит изменение конфигурации, для которого вы отключили воссоздание Activity , активность получает вызов Activity.onConfigurationChanged() . Любые присоединенные представления также получают вызов View.onConfigurationChanged() . Для изменений конфигурации, которые вы не добавили в android:configChanges , система воссоздает действие как обычно.

Метод обратного вызова onConfigurationChanged() получает объект Configuration , который определяет новую конфигурацию устройства. Прочтите поля объекта Configuration , чтобы определить, какая у вас новая конфигурация. Чтобы внести последующие изменения, обновите ресурсы, которые вы используете в своем интерфейсе. Когда система вызывает этот метод, объект Resources вашего действия обновляется и возвращает ресурсы на основе новой конфигурации. Это позволяет вам сбрасывать элементы вашего пользовательского интерфейса без перезапуска вашей активности системой.

Например, следующая реализация onConfigurationChanged() проверяет, доступна ли клавиатура:

Котлин

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
    } else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
    }
}

Ява

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
    } else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
    }
}

Если вам не нужно обновлять приложение на основе этих изменений конфигурации, вы можете вместо этого не реализовывать onConfigurationChanged() . В этом случае все ресурсы, использованные до изменения конфигурации, по-прежнему используются, и вы лишь избежали перезапуска своей деятельности. Например, телевизионное приложение может не реагировать на подключение или отсоединение клавиатуры Bluetooth.

Сохранить состояние

При использовании этого метода вы все равно должны сохранять состояние в течение обычного жизненного цикла действия. Это происходит по следующим причинам:

  • Неизбежные изменения: изменения конфигурации, которые вы не можете предотвратить, могут перезапустить ваше приложение.
  • Смерть процесса: ваше приложение должно иметь возможность обрабатывать смерть процесса, инициированную системой. Если пользователь покинет ваше приложение и приложение перейдет в фоновый режим, система может уничтожить приложение.

Реагируйте на изменения конфигурации в Jetpack Compose.

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

Объект Configuration доступен в иерархии Compose UI с локальной композицией LocalConfiguration . Всякий раз, когда он изменяется, компонуемые функции, считывающие из LocalConfiguration.current , перекомпоновываются. Сведения о том, как работают локальные переменные композиции, см. в разделе Данные локальной области с CompositionLocal .

Пример

В следующем примере составной объект отображает дату в определенном формате. Компонуемый объект реагирует на изменения конфигурации локали системы, вызывая ConfigurationCompat.getLocales() с LocalConfiguration.current .

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

Чтобы избежать воссоздания Activity при изменении языкового стандарта, Activity , в котором размещен код Compose, должно отказаться от изменений конфигурации языкового стандарта. Для этого вы устанавливаете android:configChanges в locale|layoutDirection .

Изменения конфигурации: ключевые понятия и лучшие практики

Вот ключевые понятия, которые вам необходимо знать при работе над изменениями конфигурации:

  • Конфигурации: конфигурации устройства определяют, как пользовательский интерфейс отображается пользователю, например размер дисплея приложения, языковой стандарт или системная тема.
  • Изменения конфигурации: конфигурации изменяются при взаимодействии с пользователем. Например, пользователь может изменить настройки устройства или способ физического взаимодействия с устройством. Невозможно предотвратить изменения конфигурации.
  • Воссоздание Activity : изменения конфигурации по умолчанию приводят к воссозданию Activity . Это встроенный механизм повторной инициализации состояния приложения для новой конфигурации.
  • Уничтожение Activity . При воссоздании Activity система уничтожает старый экземпляр Activity и создает на его месте новый. Старый экземпляр уже устарел. Любые оставшиеся ссылки на него приводят к утечкам памяти, ошибкам или сбоям.
  • Состояние: состояние старого экземпляра Activity отсутствует в новом экземпляре Activity , поскольку это два разных экземпляра объекта. Сохраните состояние приложения и пользователя, как описано в разделе «Сохранение состояний пользовательского интерфейса» .
  • Отказ: отказ от воссоздания активности при изменении конфигурации является потенциальной оптимизацией. Это требует, чтобы ваше приложение правильно обновлялось в ответ на новую конфигурацию.

Чтобы обеспечить удобство работы пользователей, соблюдайте следующие рекомендации:

  • Будьте готовы к частым изменениям конфигурации: не думайте, что изменения конфигурации происходят редко или никогда не происходят, независимо от уровня API, форм-фактора или набора инструментов пользовательского интерфейса. Когда пользователь вызывает изменение конфигурации, он ожидает, что приложения обновятся и продолжат правильно работать с новой конфигурацией.
  • Сохранять состояние: не терять состояние пользователя при воссоздании Activity . Сохраните состояние, как описано в разделе «Сохранение состояний пользовательского интерфейса» .
  • Избегайте отказа в качестве быстрого решения: не отказывайтесь от воссоздания Activity в качестве ярлыка, чтобы избежать потери состояния. Отказ от воссоздания активности требует от вас выполнения обещания обработать изменение, и вы все равно можете потерять состояние из-за воссоздания Activity из-за других изменений конфигурации, прекращения процесса или закрытия приложения. Полностью отключить Activity отдых невозможно. Сохраните состояние, как описано в разделе «Сохранение состояний пользовательского интерфейса» .
  • Не избегайте изменений конфигурации: не устанавливайте ограничения на ориентацию, соотношение сторон или изменение размера, чтобы избежать изменений конфигурации и воссоздания Activity . Это негативно влияет на пользователей, которые хотят использовать ваше приложение по своему усмотрению.

Обработка изменений конфигурации на основе размера

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

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

Ограничить воссоздание активности для изменений конфигурации на основе размера

Если вы отключите воссоздание Activity для изменений конфигурации на основе размера, система не воссоздает Activity . Вместо этого он получает вызов Activity.onConfigurationChanged() . Любые прикрепленные представления получают вызов View.onConfigurationChanged() .

Воссоздание Activity отключено для изменений конфигурации на основе размера, если в файле манифеста есть android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout ".

Разрешить воссоздание активности для изменений конфигурации на основе размера

В Android 7.0 (уровень API 24) и более поздних версиях воссоздание Activity происходит только для изменений конфигурации на основе размера, если изменение размера существенно. Если система не воссоздает Activity из-за недостаточного размера, она может вместо этого вызвать Activity.onConfigurationChanged() и View.onConfigurationChanged() .

Есть некоторые предостережения относительно обратных вызовов Activity и View , когда Activity не воссоздается:

  • В Android 11 (уровень API 30)–Android 13 (уровень API 33) Activity.onConfigurationChanged() не вызывается.
  • Существует известная проблема, из-за которой View.onConfigurationChanged() в некоторых случаях не может быть вызван в Android 12L (уровень API 32) и ранних версиях Android 13 (уровень API 33). Дополнительную информацию см. в этом общедоступном выпуске . С тех пор эта проблема была решена в более поздних выпусках Android 13 и Android 14.

Для кода, который зависит от прослушивания изменений конфигурации на основе размера, мы рекомендуем использовать служебную программу View с переопределенным View.onConfigurationChanged() вместо того, чтобы полагаться на воссоздание Activity или Activity.onConfigurationChanged() .