Мульти-резюме

В Android 9 (и более ранних версиях) приложения переходили в состояние PAUSED в следующих случаях:

  • Поверх приложения запускалось новое полупрозрачное окно, при этом само приложение оставалось видимым (и, следовательно, не останавливалось).
  • Активность теряла фокус, но оставалась видимой и могла взаимодействовать с ней. Например, в многооконном режиме несколько активностей могут быть видны и одновременно принимать сенсорный ввод.

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

В Android 10 все активные элементы, находящиеся в видимых стеках и имеющие фокус на верхнем уровне, находятся в состоянии RESUMED . Это улучшает совместимость с многооконным режимом и режимом MD для приложений, использующих onPause() вместо onStop() для остановки обновления пользовательского интерфейса и взаимодействия с пользователем. Это означает:

  • Оба действия в режиме разделенного экрана возобновляются.
  • В режиме свободного отображения окон возобновляется вся основная видимая активность.
  • Можно одновременно возобновить работу на нескольких экранах.

Рисунок 1. Возобновление работы нескольких приложений на складном устройстве.

Рисунок 2. Возобновление работы нескольких приложений в режиме рабочего стола.

Действия могут находиться в состоянии PAUSED , когда на них невозможно сфокусироваться или они частично заслонены, например:

  • В режиме разделенного экрана (с панелью запуска сбоку) основное приложение не возобновляет работу, поскольку оно недоступно для фокусировки.
  • В режиме «картинка в картинке» работа не возобновляется, поскольку объект недоступен для фокусировки.
  • Когда действия перекрываются другими прозрачными действиями в том же стеке.

Такой подход указывает приложениям, что активность может получать ввод от пользователя только в состоянии RESUMED . До Android 10 активности могли получать ввод и в состоянии PAUSED (например, попробуйте одновременно коснуться обеих активностей в режиме разделенного экрана на устройстве под управлением Android 9).

Чтобы сохранить сигнал возобновления работы из предыдущих версий Android (и сообщить, когда приложениям следует получить доступ к ресурсам с эксклюзивным доступом или ресурсам-синглтонам), Android 10 включает новый обратный вызов:

Activity#onTopResumedActivityChanged(boolean onTop)

При вызове этот коллбэк вызывается между Activity#onResume() и Activity#onPause() . Этот коллбэк является необязательным и может быть пропущен, поэтому активность может перейти из состояния RESUMED в PAUSED , не становясь при этом самой верхней в системе. Например, в многооконном режиме. Поскольку этот коллбэк является необязательным, он не является частью жизненного цикла активности и должен использоваться редко.

Предыдущая активность, возобновившая выполнение с верхнего уровня, получает и завершает выполнение метода onTopResumedActivity(false) до того, как следующая активность, возобновившая выполнение с верхнего уровня, получит метод onTopResumedActivity(true) если только предыдущая активность не тратит слишком много времени на обработку вызова метода и не достигает таймаута в 500 мс.

Совместимость

Для обеспечения совместимости при использовании нескольких резюме рассмотрите следующие решения.

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

  • Проблема. В Android 9 и более ранних версиях одновременно возобновляется только одна активность в системе. Все переходы между активностями включают приостановку одной активности перед возобновлением другой. Некоторые приложения и фреймворки (например, Flutter или LocalActivityManager в Android) используют этот факт и хранят состояние возобновленной активности в синглтонах.
  • Решение. В Android 9 и более ранних версиях, если возобновляется запуск двух активностей одного и того же процесса, система возобновляет только ту активность, которая находится выше в Z-порядке. Приложения, ориентированные на Android 10, могут поддерживать одновременное возобновление нескольких активностей.

Одновременный доступ к камере

  • Проблемы . Эти проблемы также присутствуют в Android 9 и более ранних версиях. Например, в режиме «картинка в картинке» при возобновлении работы приложения на весь экран фокус камеры может смещаться в сторону приостановленного приложения, находящегося сверху, но эта проблема становится более заметной при более широком использовании многооконных и многоэкранных режимов.
    • Из-за изменений в состоянии RESUME приложения могут отключаться от камеры даже в режиме возобновления работы . Для решения этой проблемы приложения должны обрабатывать отключение камеры без сбоев. При отключении приложения получают обратный вызов disconnected, и все вызовы API начинают генерировать исключение CameraAccessException .
    • resizeableActivity=false не гарантирует эксклюзивный доступ к камере, поскольку другие приложения, использующие камеру, могут быть открыты на других экранах.
  • Решения. Разработчикам следует добавить логику на случай отключения приложения от камеры. Если приложение отключается от камеры, оно должно отслеживать коллбэки доступности камеры, чтобы попытаться переподключиться и продолжить использование камеры. В дополнение к существующему коллбэку CameraManager#AvailabilityCallback#onCameraAvailable() , Android 10 добавил CameraManager#AvailabilityCallback#onCameraAccessPrioritiesChanged() , который охватывает случай, когда фокус (и приоритет камеры) переключается между несколькими возобновленными действиями. Разработчикам приложений следует использовать оба этих коллбэка, чтобы определить оптимальное время для попытки доступа к камере.

Множественное резюме

В Android 10 состояние жизненного цикла активности определяется видимостью и порядком Z-сохранения. Чтобы гарантировать корректное состояние активности после обновления видимости и определить, какое состояние жизненного цикла применимо, вызывайте метод ActivityRecord#makeActiveIfNeeded() из разных мест. В Android 10 состояние active означает либо RESUMED , либо PAUSED и работает только в этих двух случаях.

В Android 10 возобновление активности отслеживается отдельно в каждом стеке, а не в одном месте в системе. Это связано с тем, что в многооконных режимах может одновременно выполняться несколько переходов между активностями. Подробнее см. ActivityStack#mInResumeTopActivity .

Обратный вызов возобновленной активности в верхней части экрана

После действий, которые могут привести к изменению состояния верхнего уровня активности (например, запуск активности, возобновление или изменение порядка Z), вызывается ActivityStackSupervisor#updateTopResumedActivityIfNeeded() . Этот метод проверяет, изменилось ли состояние самого верхнего возобновленного уровня активности, и при необходимости выполняет обновление. Если предыдущая активность верхнего уровня, которая возобновила работу, не освободила свое состояние, ей отправляется сообщение о потере состояния верхнего уровня, и на стороне сервера устанавливается тайм-аут ( ActivityStackSupervisor#scheduleTopResumedStateLossTimeout() ). Отчет о состоянии верхнего уровня отправляется следующей активности после того, как предыдущая освободила состояние, или когда истекает тайм-аут (см. примеры использования:

ActivityStackSupervisor#scheduleTopResumedActivityStateIfNeeded()

Добавлен новый элемент транзакции TopResumedActivityChangeItem для отправки клиентам информации об изменениях состояния, которые были возобновлены, и он использует архитектуру ActivityLifecycler из Android 9.

Состояние возобновления активности хранится на стороне клиента, и каждый раз, когда активность переходит в состояние RESUMED или PAUSED проверяется также, следует ли вызывать функцию обратного вызова onTopResumedActivityChanged() . Это обеспечивает определенную развязку в обмене данными о состояниях жизненного цикла и состоянии возобновления активности между сервером и клиентом.