Android 11 (уровень API 30) и выше поддерживает функцию «заморозки кэшированных приложений». Эта функция останавливает выполнение кэшированных процессов и снижает потребление ресурсов, предотвращая некорректную работу приложений, которые могут пытаться работать в кэшированном состоянии.
Функция «заморозки кэшированных приложений» сохраняет приложения в оперативной памяти, не задействуя при этом процессор. Если Android определяет, что приложение не должно выполнять свою работу, но может понадобиться в будущем, он замораживает процесс приложения, а не завершает его. Это предотвращает «холодный запуск», когда приложение снова понадобится.
Android блокирует кэшированные приложения, перенося их процессы в замороженную группу процессов (cgroup). Это снижает потребление ресурсов ЦП как в активном, так и в неактивном состоянии при наличии активных кэшированных приложений. Заморозку приложений можно включить с помощью системного флага конфигурации или параметра разработчика.
В Android 14 (уровень API 34) и выше функция заморозки кэшированных приложений включает в себя следующие надежные механизмы:
- Процессы приложения, находящиеся в кэшированном состоянии, приостанавливаются через 10 секунд после перехода в кэшированное состояние.
- Система мгновенно размораживает зависший процесс приложения во время события жизненного цикла. К таким событиям относятся получение намерения , запуск службы заданий или возобновление пользователем своей деятельности .
ActivityManagerService управляет всеми процессами приложения и принимает решения, касающиеся жизненного цикла приложения. CachedAppOptimizer отвечает за приостановку процесса приложения.
Когда процесс приложения зависает, все его потоки приостанавливаются и не могут выполнять задачи ЦП, пока не разморозятся. В результате приложение не может выполнять сборку мусора (GC) и не может реагировать на события очистки памяти. Подробнее см. ComponentCallbacks2.onTrimMemory(int) . Для решения этой проблемы, начиная с Android 14:
- Приложения, имеющие видимый экземпляр
Activity, получают уведомление о событииTRIM_MEMORY_UI_HIDDENкак только переходят в фоновый режим. Приложения, остающиеся в жизненном цикле без пользовательского интерфейса, например, приложения с фоновой службой, могут получать уведомлениеTRIM_MEMORY_BACKGROUND. Другие события TRIM_MEMORY_BACKGROUND не доставляются, поскольку, когда приложения имеют право на получение этих событий, ожидается, что они будут заморожены. - Вскоре после перехода в кэшированное состояние система может запросить у среды выполнения приложения сборку мусора в рамках подготовки к возможному зависанию.
- Когда процесс приложения зависает, могут происходить дополнительные шаги по уплотнению памяти, такие как запись измененных страниц в резервное хранилище и подмена анонимных страниц на ZRAM.
- Если все процессы для конкретного приложения зависают, система разрывает все активные TCP-сокеты, поддерживаемые приложением. Это предотвращает отправку сервером TCP-пингов keepalive, которые могли бы разбудить модем устройства.
Кэшированные процессы приложения размораживаются, когда их состояние процесса повышается с кэшированного на состояние более высокой важности. Чтобы уменьшить количество событий разморозки в Android 14 и выше, система ставит в очередь широковещательные сообщения, зарегистрированные в контексте, пока приложение находится в кэшированном состоянии. Широковещательные сообщения, зарегистрированные в контексте, — это приемники, которые приложение динамически регистрирует, вызывая Context.registerReceiver . Система доставляет эти сообщения из очереди только после разморозки приложения. В отличие от этого, система не ставит в очередь широковещательные сообщения, объявленные в манифесте. Широковещательные сообщения, объявленные в манифесте, — это приемники, статически объявленные в AndroidManifest.xml с помощью элемента <receiver> . Система немедленно размораживает кэшированное приложение для доставки широковещательных сообщений, объявленных в манифесте.
Влияние на здоровье системы
Android завершает работу наименее используемого кэшированного процесса приложения, если количество кэшированных процессов превышает MAX_CACHED_PROCESSES . На поддерживаемых устройствах под управлением Android 14 и выше значение MAX_CACHED_PROCESSES значительно увеличено, что позволяет устройствам поддерживать существенно больше кэшированных процессов приложений в оперативной памяти.
Сохранение большего количества приложений в кэше оперативной памяти приводит к снижению количества холодных запусков до 30%, при этом снижение зависит от общего объема оперативной памяти устройства. Одновременно с этим минимизируется потребление ресурсов процессора кэшированными приложениями, что приводит к значительной экономии заряда батареи.
Исключения для морозильных камер
При определенных условиях процесс приложения может перейти в кэшированное состояние, но при этом остаться незамороженным. Эти исключения являются деталями реализации и могут измениться в будущих версиях Android:
- Блокировка файлов: Если кэшированный процесс удерживает блокировку файла, которая блокирует другие некэшированные процессы, то процесс, удерживающий блокировку, не блокируется.
- Привязки
BIND_WAIVE_PRIORITY: Процессы приложений с входящими привязками, созданными с помощьюContext.BIND_WAIVE_PRIORITY, могут перейти в кэшированное состояние, но оставаться незамороженными до тех пор, пока все подключенные клиентские процессы также не будут кэшированы. Это исключение поддерживает многопроцессные приложения, такие как веб-браузеры, использующие пользовательские вкладки.
Внедрить функцию "морозки приложений"
Функция «Заморозка кэшированных приложений» использует функцию «Заморозка cgroup v2» ядра. Устройства, поставляемые с совместимым ядром, могут её включить. Включите параметр разработчика «Приостановить выполнение для кэшированных приложений» или установите флаг конфигурации устройства activity_manager_native_boot use_freezer в true . Например:
adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot Функция заморозки отключается, если установить флаг use_freezer в false или отключить параметры разработчика. Например:
adb shell device_config put activity_manager_native_boot use_freezer false && adb rebootЭту настройку можно изменить, внеся изменения в конфигурацию устройства в обновлении или новой версии программного обеспечения.
Чтобы переопределить MAX_CACHED_PROCESSES , например, установив значение 1024 для тестирования:
adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistent Чтобы отменить переопределение параметра MAX_CACHED_PROCESSES :
adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests none Apps Freezer не предоставляет официальных API и не имеет эталонной реализации клиента, но использует скрытые системные API setProcessFrozen для заморозки отдельного процесса и enableFreezer для включения или отключения заморозки на глобальном уровне.
Обработка пользовательских функций
Предполагается, что процессы приложений не будут выполнять никакой работы, пока данные находятся в кэше, но некоторые приложения могут иметь пользовательские функции, поддерживаемые процессами, которые должны работать в кэшированном состоянии. Когда на устройстве, на котором запущены такие приложения, включена функция «заморозки приложений», кэшированные процессы замораживаются, что может препятствовать работе пользовательских функций.
В качестве обходного пути можно изменить статус процесса на «некэшируемый» до того, как процессу потребуется выполнить какую-либо работу. Это изменение позволит приложениям оставаться активными. Примерами активных статусов являются привязанная служба переднего плана или статус переднего плана.
Типичные виды отказов
Когда процессы приложения заморожены, некорректное межпроцессное взаимодействие (IPC) или планирование задач могут привести к завершению работы приложения или непредвиденному поведению.
Синхронные операции связывания для замороженных процессов
Когда процесс клиентского приложения отправляет синхронную транзакцию связывания процессу серверного приложения, находящемуся в состоянии ожидания, система немедленно завершает работу процесса серверного приложения. Это предотвращает бесконечную блокировку потока клиента в ожидании ответа от зависшего сервера. Затем поток клиента получает RemoteException , и срабатывают все зарегистрированные обработчики событий. Подробнее см. IBinder.linkToDeath .
Первопричина: Эта ошибка обычно вызвана ошибкой в клиентском приложении. Когда клиент подключается к службе, процесс сервера привязывается к клиенту и не может перейти в кэшированное состояние до того, как это сделает клиент. Подробнее см. Context.bindService . Однако, как только клиент вызывает Context.unbindService , процесс сервера может быть кэширован и завис. Если клиент продолжает использовать кэшированную ссылку IBinder после отмены привязки, он рискует взаимодействовать с зависшим процессом.
Чтобы избежать этой проблемы, убедитесь, что клиентские приложения удаляют ссылки IBinder сразу после вызова Context.unbindService .
Переполнение буфера асинхронной транзакции связывания
Когда процесс серверного приложения получает асинхронные ( oneway ) транзакции связывания в замороженном состоянии, эти транзакции буферизуются в буфере для каждого процесса. Если сервер получает слишком много асинхронных транзакций в замороженном состоянии, буфер переполняется, и система завершает работу процесса серверного приложения.
Чтобы предотвратить переполнение буфера, избегайте отправки чрезмерного количества асинхронных транзакций связывания процессам, которые могут быть кэшированы или заморожены.
Повторное выполнение запланированных задач после размораживания
Если приложение выполняет повторяющиеся задачи, они приостанавливаются на время замораживания процесса. Подробности см. в ScheduledThreadPoolExecutor.scheduleAtFixedRate или Timer.scheduleAtFixedRate . После размораживания процесса накопленные пропущенные выполнения могут выполняться быстро одно за другим практически без задержки.
Чтобы предотвратить резкое увеличение количества выполнений при разблокировке приложения, используйте scheduleWithFixedDelay вместо scheduleAtFixedRate для фоновых задач. Также можно использовать WorkManager .
Проверьте и устраните неполадки, связанные с зависанием приложений.
Для проверки корректной работы функции заморозки приложений или для устранения неполадок, связанных с заморозкой, используйте следующие диагностические инструменты и команды:
Команды диспетчера действий
С помощью команд adb shell am можно вручную управлять заморозкой и уплотнением для конкретного процесса:
Принудительное приостановление процесса:
adb shell am freeze <process>Принудительно разморозить процесс:
adb shell am unfreeze <process>Принудительное полное сжатие памяти для процесса:
adb shell am compact full <process>
Проверка Logcat
Просмотрите logcat, чтобы увидеть замороженные и размороженные записи каждый раз, когда процесс переходит в режим заморозки или из него:
adb logcat | grep -i "\(freezing\|froze\)" В журналах причин разморозки выводятся перечисляемые значения из перечисления буфера протокола UnfreezeReason .
Осмотр Дампси
Проверьте список зависших процессов с помощью dumpsys activity :
adb shell dumpsys activity | grep -A 20 "Apps frozen:"Проверьте наличие файла /sys/fs/cgroup/uid_0/cgroup.freeze .
ApplicationExitInfo
Чтобы узнать причину завершения предыдущего процесса, см. ActivityManager.getHistoricalProcessExitReasons . Если процесс приложения был завершен из-за проблемы, связанной с заморозкой, например, из-за получения синхронной транзакции связывания во время заморозки, причина завершения устанавливается в ApplicationExitInfo.REASON_FREEZER .
трассировка Перфетто
События, связанные с Freezer, передаются в дорожку с именем Freezer в рамках процесса system_server в трассировках Perfetto:
- Срезы
FreezeиUnfreezeуказывают на момент изменения состояния процесса. - События
updateAppFreezeStateLSPпоказывают, когда системный сервер повторно проверяет атрибуты процесса для принятия решения о замораживании или размораживании.
Вы можете просматривать эти события непосредственно в пользовательском интерфейсе Perfetto или анализировать их с помощью PerfettoSQL:
INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");
В стандартной библиотеке PerfettoSQL события, связанные с заморозкой данных, также суммируются в таблице android_freezer_events .