Обработка изменений в режиме выполнения

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

  • Размер экрана приложения
  • Разрешение экрана (масштаб)
  • Ориентация экрана
  • Размер и насыщенность шрифта
  • Региональные настройки
  • Тёмная и светлая темы
  • Доступность клавиатуры

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

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

Отдых

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

Обычно Activity выступает в качестве хоста для composable-функций. Когда Activity будет создана заново, Compose также воссоздаст пользовательский интерфейс, используя новые значения конфигурации.

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

Пример для развлекательного центра

Рассмотрим composable-функцию, которая показывает статический заголовок, используя строковый ресурс:

// In the res/values/strings.xml file
// <string name="compose">Jetpack Compose</string>

// In your Compose code
Text(
    text = stringResource(R.string.compose)
)

При создании Activity composable-функция Text считывает текущую конфигурацию (например, язык) и разрешает соответствующий строковый ресурс.

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

При этом также очищаются все состояния, сохраненные в виде полей в Activity.

Чтобы сохранить состояние интерфейса при изменении конфигурации, используйте рекомендуемые шаблоны управления состоянием. Используйте ViewModel для данных и бизнес-логики, а rememberSaveable – для состояния на уровне интерфейса. С помощью этих механизмов ваше состояние переживает воссоздание Activity, в то время как пользовательский интерфейс обновляется, чтобы отразить новую конфигурацию.

Подробнее о том, как сохранять состояние интерфейса в Compose…

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

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

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

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

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

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

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

Ограничить повторное создание действий

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

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

Обработка изменений конфигурации непосредственно в Compose имеет несколько преимуществ:

  • Повышение производительности. Перекомпоновка пользовательского интерфейса требует меньше ресурсов, чем полный цикл воссоздания 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 17

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

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

<activity
    android:name=".MyActivity"
    android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation|colorMode|touchscreen|...">
    ...
</activity>

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

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

Объект Configuration доступен в иерархии Compose UI с локальным компонентом LocalConfiguration. При каждом изменении composable-функции, считывающие данные из LocalConfiguration.current, перекомпонуются. Подробнее о том, как работают локальные композиции…

Пример

В примере ниже composable-функция показывает дату в определенном формате. Composable-функция реагирует на изменения в настройках региональных стандартов системы, вызывая 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.

Изменения конфигурации: основные понятия и рекомендации

Ниже приведены основные понятия, которые необходимо знать при работе с изменениями конфигурации.

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

Чтобы пользователям было удобно, следуйте приведенным ниже рекомендациям.

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

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

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

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

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

Если вы отключите повторное создание Activity при изменении конфигурации, связанной с размером, система не будет повторно создавать Activity. Вместо этого вызывается метод Activity.onConfigurationChanged. Любые composable-функции, считывающие LocalConfiguration.current, автоматически перекомпонуются, чтобы отразить новый размер.

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

Дополнительные ресурсы

Дополнительную информацию об обработке изменений конфигурации можно найти в следующих ресурсах:

Документация

Просмотр контента