Некоторые параметры устройства могут изменяться во время работы приложения. К ним относятся, помимо прочего:
- Размер экрана приложения
- Ориентация экрана
- Размер и толщина шрифта
- Местоположение
- Темный режим против светлого режима
- Наличие клавиатуры
Большинство этих изменений конфигурации происходит в результате взаимодействия с пользователем. Например, поворот или складывание устройства изменяет объем экранного пространства, доступного для вашего приложения. Аналогично, изменение настроек устройства, таких как размер шрифта, язык или предпочтительная тема, изменяет соответствующие значения в объекте Configuration .
Эти параметры обычно требуют достаточно существенных изменений в пользовательском интерфейсе вашего приложения, поэтому платформа Android имеет специальный механизм для обработки таких изменений. Этот механизм называется пересозданием Activity .
Активный отдых
Система пересоздает Activity при изменении конфигурации. Для этого система вызывает onDestroy() и уничтожает существующий экземпляр Activity . Затем она создает новый экземпляр с помощью onCreate() , и этот новый экземпляр Activity инициализируется новой, обновленной конфигурацией. Это также означает, что система пересоздает пользовательский интерфейс с новой конфигурацией.
Функция перезаписи помогает вашему приложению адаптироваться к новым конфигурациям, автоматически перезагружая его с использованием альтернативных ресурсов, соответствующих новой конфигурации устройства.
Пример отдыха
Рассмотрим TextView , отображающий статический заголовок с помощью android:text="@string/title" , как определено в XML-файле разметки. При создании представления текст устанавливается ровно один раз, в зависимости от текущего языка. Если язык меняется, система пересоздает активность. Следовательно, система также пересоздает представление и инициализирует его правильным значением в соответствии с новым языком.
При создании нового объекта Activity также удаляется любое состояние, хранящееся в виде полей в Activity или в любом из содержащихся в нем Fragment , View или других объектах. Это происходит потому, что создание нового объекта Activity создает совершенно новый экземпляр Activity и пользовательского интерфейса. Кроме того, старый Activity больше не виден и недействителен, поэтому любые оставшиеся ссылки на него или содержащиеся в нем объекты устаревают. Это может привести к ошибкам, утечкам памяти и сбоям.
Ожидания пользователей
Пользователь приложения ожидает сохранения состояния. Если пользователь заполняет форму и открывает другое приложение в многооконном режиме для просмотра информации, возвращение к очищенной форме или в другое место приложения будет считаться плохим пользовательским опытом. Как разработчик, вы должны обеспечить согласованный пользовательский опыт как при изменении конфигурации, так и при воссоздании действий.
Чтобы проверить, сохраняется ли состояние в вашем приложении, вы можете выполнять действия, которые приводят к изменению конфигурации как в фоновом, так и в активном режиме работы приложения. К таким действиям относятся:
- Вращение устройства
- Переход в многооконный режим
- Изменение размера приложения в многооконном режиме или в окне произвольной формы.
- Складывание складного устройства с несколькими дисплеями.
- Изменение темы оформления системы, например, переключение с темного режима на светлый.
- Изменение размера шрифта
- Изменение системного языка или языка приложения
- Подключение или отключение аппаратной клавиатуры
- Подключение или отключение док-станции
Существует три основных подхода к сохранению соответствующего состояния при воссоздании Activity . Выбор подхода зависит от типа состояния, которое вы хотите сохранить:
- Локальное хранение данных позволяет обрабатывать сбои процессов при работе со сложными или большими объемами данных. К постоянному локальному хранилищу относятся базы данных или
DataStore. - Для управления состоянием пользовательского интерфейса в памяти во время активного использования приложения пользователем сохраняются такие объекты , как экземпляры
ViewModel. - Сохраненное состояние экземпляра используется для обработки завершения процессов, инициированного системой, и поддержания временного состояния, зависящего от ввода пользователя или навигации.
Чтобы подробно ознакомиться с API для каждого из этих методов и узнать, когда целесообразно использовать тот или иной, см. раздел «Сохранение состояний пользовательского интерфейса» .
Ограничить рекреационную деятельность
Вы можете предотвратить автоматическое пересоздание Activity при определенных изменениях конфигурации. Пересоздание Activity приводит к повторному созданию всего пользовательского интерфейса и любых объектов, производных от Activity . У вас могут быть веские причины избегать этого. Например, вашему приложению может не потребоваться обновление ресурсов во время определенного изменения конфигурации, или у вас могут быть ограничения производительности. В этом случае вы можете объявить, что ваше 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 получает вызов Activity.onConfigurationChanged() . Любые прикрепленные представления также получают вызов View.onConfigurationChanged() . Для изменений конфигурации, которые вы не добавили в android:configChanges , система пересоздает Activity как обычно.
Метод обратного вызова 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()
}
}
Java
@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 с помощью локального объекта композиции 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 заново. Вместо этого она получает вызов Activity.onConfigurationChanged() . Любые прикрепленные представления получают вызов View.onConfigurationChanged() .
При изменении конфигурации на основе размера, если в файле манифеста указано android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" пересоздание Activity отключается.
Разрешить повторное создание активности при изменении конфигурации в зависимости от размера.
В Android 7.0 (уровень API 24) и выше пересоздание Activity происходит только при изменении размера, если это изменение существенно. Если система не пересоздает Activity из-за недостаточного размера, она может вместо этого вызвать Activity.onConfigurationChanged() и View.onConfigurationChanged() .
При создании Activity без повторного создания необходимо учитывать некоторые нюансы, касающиеся коллбэков Activity и View :
- В 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() .