Фоновые процессы могут занимать много памяти и батареи. Например, неявная широковещательная рассылка может запустить множество фоновых процессов, зарегистрированных для ее прослушивания, даже если эти процессы могут не выполнять большой работы. Это может оказать существенное влияние как на производительность устройства, так и на удобство использования.
Чтобы решить эту проблему, Android 7.0 (уровень API 24) применяет следующие ограничения:
- Приложения, предназначенные для Android 7.0 (уровень API 24) и более поздних версий, не получают широковещательные сообщения
CONNECTIVITY_ACTION
, если они объявляют свой получатель широковещательных сообщений в манифесте. Приложения по-прежнему будут получать широковещательные сообщенияCONNECTIVITY_ACTION
, если они зарегистрируют свойBroadcastReceiver
с помощьюContext.registerReceiver()
и этот контекст все еще действителен. - Приложения не могут отправлять и получать трансляции
ACTION_NEW_PICTURE
илиACTION_NEW_VIDEO
. Эта оптимизация затрагивает все приложения, а не только те, которые ориентированы на Android 7.0 (уровень API 24).
Если ваше приложение использует какое-либо из этих намерений, вам следует как можно скорее удалить зависимости от них, чтобы можно было правильно ориентировать устройства под управлением Android 7.0 или более поздней версии. Платформа Android предоставляет несколько решений, позволяющих снизить потребность в этих неявных широковещательных передачах. Например, JobScheduler
и новый WorkManager предоставляют надежные механизмы для планирования сетевых операций при выполнении определенных условий, таких как подключение к сети без учета трафика. Теперь вы также можете использовать JobScheduler
для реагирования на изменения поставщиков контента. Объекты JobInfo
инкапсулируют параметры, которые JobScheduler
использует для планирования вашего задания. Когда условия задания выполняются, система выполняет это задание в JobService
вашего приложения.
На этой странице мы узнаем, как использовать альтернативные методы, такие как JobScheduler
, чтобы адаптировать ваше приложение к этим новым ограничениям.
Ограничения, инициируемые пользователем
На странице «Использование батареи» в настройках системы пользователь может выбрать один из следующих вариантов:
- Без ограничений: разрешить всю работу в фоновом режиме, которая может потреблять больше заряда батареи.
- Оптимизировано (по умолчанию). Оптимизация возможности приложения выполнять фоновую работу в зависимости от того, как пользователь взаимодействует с приложением.
- Ограничено: полностью запрещает запуск приложения в фоновом режиме. Приложения могут работать не так, как ожидалось.
Если приложение демонстрирует некорректное поведение, описанное в Android Vitals , система может предложить пользователю ограничить доступ этого приложения к системным ресурсам.
Если система замечает, что приложение потребляет слишком много ресурсов, она уведомляет пользователя и дает ему возможность ограничить действия приложения. Поведения, которые могут вызвать уведомление, включают в себя:
- Чрезмерные блокировки пробуждения: 1 частичная блокировка пробуждения удерживается в течение часа, когда экран выключен.
- Чрезмерное количество фоновых служб: если приложение нацелено на уровни API ниже 26 и имеет слишком много фоновых служб.
Точные налагаемые ограничения определяются производителем устройства. Например, в сборках AOSP под управлением Android 9 (уровень API 28) или выше приложения, работающие в фоновом режиме и находящиеся в «ограниченном» состоянии, имеют следующие ограничения:
- Не могу запустить службы переднего плана
- Существующие службы переднего плана удаляются с переднего плана.
- Сигнализация не срабатывает
- Задания не выполняются
Кроме того, если приложение предназначено для Android 13 (уровень API 33) или выше и находится в «ограниченном» состоянии, система не передает широковещательную рассылку BOOT_COMPLETED
или LOCKED_BOOT_COMPLETED
до тех пор, пока приложение не будет запущено по другим причинам.
Конкретные ограничения перечислены в разделе «Ограничения управления питанием» .
Ограничения на получение трансляций сетевой активности
Приложения, предназначенные для Android 7.0 (уровень API 24), не получают широковещательные сообщения CONNECTIVITY_ACTION
, если они регистрируются для их получения в своем манифесте, и процессы, зависящие от этого широковещательного сообщения, не запускаются. Это может создать проблему для приложений, которые хотят прослушивать изменения в сети или выполнять массовые сетевые действия, когда устройство подключается к сети без ограничений. В платформе Android уже существует несколько решений, позволяющих обойти это ограничение, но выбор правильного зависит от того, чего вы хотите от своего приложения.
Примечание. BroadcastReceiver
зарегистрированный с помощью Context.registerReceiver()
продолжает получать эти широковещательные сообщения во время работы приложения.
Планирование сетевых заданий на нелимитированных соединениях
При использовании класса JobInfo.Builder
для создания объекта JobInfo
примените метод setRequiredNetworkType()
и передайте JobInfo.NETWORK_TYPE_UNMETERED
в качестве параметра задания. В следующем примере кода планируется запуск службы, когда устройство подключается к сети без счетчиков и заряжается:
Котлин
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java) ) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build() jobScheduler.schedule(job) }
Ява
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MyJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build(); js.schedule(job); }
Когда условия вашего задания выполняются, ваше приложение получает обратный вызов для запуска метода onStartJob()
в указанном JobService.class
. Дополнительные примеры реализации JobScheduler
см. в примере приложения JobScheduler .
Новой альтернативой JobScheduler является WorkManager, API, который позволяет планировать фоновые задачи, требующие гарантированного завершения, независимо от того, запущен процесс приложения или нет. WorkManager выбирает подходящий способ запуска работы (либо непосредственно в потоке процесса вашего приложения, либо с помощью JobScheduler, FirebaseJobDispatcher или AlarmManager) на основе таких факторов, как уровень API устройства. Кроме того, WorkManager не требует служб Play и предоставляет несколько расширенных функций, таких как объединение задач в цепочку или проверка статуса задачи. Чтобы узнать больше, см. WorkManager .
Отслеживайте сетевое подключение во время работы приложения
Запущенные приложения по-прежнему могут прослушивать CONNECTIVITY_CHANGE
с зарегистрированным BroadcastReceiver
. Однако API ConnectivityManager
предоставляет более надежный метод запроса обратного вызова только при выполнении определенных условий сети.
Объекты NetworkRequest
определяют параметры сетевого обратного вызова с точки зрения NetworkCapabilities
. Объекты NetworkRequest
создаются с помощью класса NetworkRequest.Builder
. registerNetworkCallback()
затем передает объект NetworkRequest
в систему. Когда условия сети выполняются, приложение получает обратный вызов для выполнения метода onAvailable()
определенного в его классе ConnectivityManager.NetworkCallback
.
Приложение продолжает получать обратные вызовы до тех пор, пока приложение не закроется или не вызовет unregisterNetworkCallback()
.
Ограничения на прием изображений и видеотрансляций
В Android 7.0 (уровень API 24) приложения не могут отправлять или получать трансляции ACTION_NEW_PICTURE
или ACTION_NEW_VIDEO
. Это ограничение помогает снизить влияние на производительность и удобство работы пользователя, когда нескольким приложениям приходится выходить из режима ожидания для обработки нового изображения или видео. Android 7.0 (уровень API 24) расширяет возможности JobInfo
и JobParameters
предоставляя альтернативное решение.
Запуск заданий при изменении URI контента
Чтобы запускать задания при изменении URI контента, Android 7.0 (уровень API 24) расширяет API JobInfo
следующими методами:
-
JobInfo.TriggerContentUri()
- Инкапсулирует параметры, необходимые для запуска задания при изменении URI контента.
-
JobInfo.Builder.addTriggerContentUri()
- Передает объект
TriggerContentUri
вJobInfo
.ContentObserver
отслеживает URI инкапсулированного контента. Если с заданием связано несколько объектовTriggerContentUri
, система обеспечивает обратный вызов, даже если она сообщает об изменении только одного из URI контента. - Добавьте флаг
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
чтобы запустить задание, если какие-либо потомки данного URI изменятся. Этот флаг соответствует параметруnotifyForDescendants
, переданному вregisterContentObserver()
.
Примечание. TriggerContentUri()
нельзя использовать в сочетании с setPeriodic()
или setPersisted()
. Чтобы постоянно отслеживать изменения содержимого, запланируйте новый JobInfo
до того, как JobService
приложения завершит обработку самого последнего обратного вызова.
Следующий пример кода планирует запуск задания, когда система сообщает об изменении URI контента, MEDIA_URI
:
Котлин
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MediaContentJob::class.java) ) .addTriggerContentUri( JobInfo.TriggerContentUri( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS ) ) .build() jobScheduler.schedule(job) }
Ява
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MediaContentJob.class)); builder.addTriggerContentUri( new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)); js.schedule(builder.build()); }
Когда система сообщает об изменении указанных URI контента, ваше приложение получает обратный вызов, и объект JobParameters
передается методу onStartJob()
в MediaContentJob.class
.
Определите, какие органы управления контентом инициировали задание
Android 7.0 (уровень API 24) также расширяет JobParameters
, позволяя вашему приложению получать полезную информацию о том, какие органы управления контентом и URI инициировали задание:
-
Uri[] getTriggeredContentUris()
- Возвращает массив URI, которые запустили задание. Это значение будет
null
если задание не было запущено ни одним URI (например, задание было запущено из-за крайнего срока или по какой-либо другой причине), либо количество измененных URI превышает 50. -
String[] getTriggeredContentAuthorities()
- Возвращает массив строк органов управления контентом, которые инициировали задание. Если возвращаемый массив не имеет
null
, используйтеgetTriggeredContentUris()
чтобы получить сведения о том, какие URI изменились.
Следующий пример кода переопределяет метод JobService.onStartJob()
и записывает центры управления контентом и URI, которые инициировали задание:
Котлин
override fun onStartJob(params: JobParameters): Boolean { StringBuilder().apply { append("Media content has changed:\n") params.triggeredContentAuthorities?.also { authorities -> append("Authorities: ${authorities.joinToString(", ")}\n") append(params.triggeredContentUris?.joinToString("\n")) } ?: append("(No content)") Log.i(TAG, toString()) } return true }
Ява
@Override public boolean onStartJob(JobParameters params) { StringBuilder sb = new StringBuilder(); sb.append("Media content has changed:\n"); if (params.getTriggeredContentAuthorities() != null) { sb.append("Authorities: "); boolean first = true; for (String auth : params.getTriggeredContentAuthorities()) { if (first) { first = false; } else { sb.append(", "); } sb.append(auth); } if (params.getTriggeredContentUris() != null) { for (Uri uri : params.getTriggeredContentUris()) { sb.append("\n"); sb.append(uri); } } } else { sb.append("(No content)"); } Log.i(TAG, sb.toString()); return true; }
Дальнейшая оптимизация вашего приложения
Оптимизация ваших приложений для работы на устройствах с низким объемом памяти или в условиях нехватки памяти может повысить производительность и удобство использования. Удаление зависимостей от фоновых служб и неявных приемников широковещательной рассылки, зарегистрированных в манифесте, может помочь вашему приложению лучше работать на таких устройствах. Хотя Android 7.0 (уровень API 24) предпринимает шаги для устранения некоторых из этих проблем, рекомендуется оптимизировать приложение для работы без использования этих фоновых процессов.
Следующие команды Android Debug Bridge (ADB) могут помочь вам протестировать поведение приложения с отключенными фоновыми процессами:
- Чтобы смоделировать условия, когда неявные широковещательные и фоновые службы недоступны, введите следующую команду:
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- Чтобы повторно включить неявные широковещательные и фоновые службы, введите следующую команду:
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
- Вы можете имитировать пользователя, переводящего ваше приложение в «ограниченное» состояние для фонового использования батареи. Этот параметр не позволяет вашему приложению работать в фоновом режиме. Для этого выполните следующую команду в окне терминала:
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny