В руководстве по началу работы было рассказано, как создать WorkRequest
и поставить его в очередь.
В этом руководстве вы узнаете, как определять и настраивать объекты WorkRequest
для обработки распространенных случаев использования, например:
- Планирование разовых и повторяющихся работ
- Установите ограничения на работу, например, требование наличия Wi-Fi или зарядки.
- Гарантия минимальной задержки в выполнении работ
- Установите стратегии повторных попыток и отсрочек
- Передать входные данные в работу
- Группируйте связанную работу вместе, используя теги
Обзор
Работа определяется в WorkManager с помощью WorkRequest
. Чтобы запланировать любую работу с помощью WorkManager, необходимо сначала создать объект WorkRequest
и поставить его в очередь.
Котлин
val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)
Ява
WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);
Объект WorkRequest
содержит всю информацию, необходимую WorkManager для планирования и выполнения вашей работы. Он включает ограничения, которые должны быть соблюдены для выполнения вашей работы, информацию о планировании, такую как задержки или интервалы повторения, конфигурацию повторов, а также может включать входные данные, если ваша работа зависит от них.
WorkRequest
сам по себе является абстрактным базовым классом. Существуют две производные реализации этого класса, которые можно использовать для создания запроса: OneTimeWorkRequest
и PeriodicWorkRequest
. Как следует из их названий, OneTimeWorkRequest
полезен для планирования неповторяющихся задач, тогда как PeriodicWorkRequest
больше подходит для планирования задач, повторяющихся с определённым интервалом.
График разовых работ
Для базовой работы, не требующей дополнительной настройки, используйте статический метод from
:
Котлин
val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
Ява
WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);
Для более сложных работ можно воспользоваться конструктором:
Котлин
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
// Additional configuration
.build()
Ява
WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
// Additional configuration
.build();
График ускоренной работы
В WorkManager 2.7.0 представлена концепция ускоренной работы. Это позволяет WorkManager выполнять важную работу, предоставляя системе лучший контроль над доступом к ресурсам.
Ускоренная работа отличается следующими характеристиками:
- Важность : ускоренная работа подходит для задач, которые важны для пользователя или инициированы пользователем.
- Скорость : ускоренная работа лучше всего подходит для коротких задач, которые начинаются немедленно и завершаются в течение нескольких минут.
- Квоты : Системная квота, ограничивающая время выполнения переднего плана, определяет, может ли запуститься ускоренное задание.
- Управление питанием : ограничения управления питанием , такие как Battery Saver и Doze, с меньшей вероятностью повлияют на ускоренную работу.
- Задержка : система немедленно выполняет ускоренные задачи, если текущая нагрузка позволяет это сделать. Это означает, что они чувствительны к задержке и не могут быть запланированы для более позднего выполнения.
Потенциальный вариант использования ускоренной работы может быть в чат-приложении, когда пользователь хочет отправить сообщение или прикрепить изображение. Аналогично, приложение, обрабатывающее платежи или подписки, также может использовать ускоренную работу. Это связано с тем, что эти задачи важны для пользователя, выполняются быстро в фоновом режиме, должны начаться немедленно и должны продолжаться, даже если пользователь закроет приложение.
Квоты
Система должна выделить время на выполнение ускоренного задания, прежде чем оно сможет быть запущено. Время выполнения не безгранично. Каждое приложение получает квоту времени выполнения. Когда приложение использует выделенное время выполнения и достигает выделенной квоты, выполнение ускоренного задания прекращается до тех пор, пока квота не обновится. Это позволяет Android более эффективно распределять ресурсы между приложениями.
Количество времени выполнения, доступное приложению, зависит от времени ожидания и важности процесса.
Вы можете определить, что происходит, когда квота выполнения не позволяет выполнить ускоренное задание немедленно. Подробности см. в следующих фрагментах.
Выполнение ускоренной работы
Начиная с WorkManager 2.7, ваше приложение может вызывать setExpedited()
, чтобы объявить, что WorkRequest
должен быть выполнен максимально быстро, используя ускоренное задание. Следующий фрагмент кода демонстрирует пример использования setExpedited()
:
Котлин
val request = OneTimeWorkRequestBuilder<SyncWorker>()
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build()
WorkManager.getInstance(context)
.enqueue(request)
Ява
OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
.setInputData(inputData)
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build();
В этом примере мы инициализируем экземпляр OneTimeWorkRequest
и вызываем для него setExpedited()
. После этого запрос становится ускоренным. Если квота позволяет, он немедленно начнёт выполняться в фоновом режиме. Если квота была использована, параметр OutOfQuotaPolicy
указывает, что запрос следует выполнить как обычный, неускоренный.
Обратная совместимость и приоритетные службы
Для поддержания обратной совместимости для ускоренных заданий WorkManager может запускать службу переднего плана на платформах версий старше Android 12. Службы переднего плана могут отображать уведомление пользователю.
Методы getForegroundInfoAsync()
и getForegroundInfo()
в Worker позволяют WorkManager отображать уведомление при вызове setExpedited()
в версиях до Android 12.
Любой ListenableWorker
должен реализовать метод getForegroundInfo
, если вы хотите запросить запуск задачи как ускоренного задания.
При использовании Android 12 или выше службы переднего плана остаются доступными через соответствующий метод setForeground
.
Рабочий
Исполнители не знают, ускорена ли выполняемая ими работа. Однако в некоторых версиях Android исполнители могут отображать уведомление об ускорении выполнения WorkRequest
.
Для этого WorkManager предоставляет метод getForegroundInfoAsync()
, который необходимо реализовать, чтобы WorkManager мог отображать уведомление о необходимости запуска ForegroundService
при необходимости.
CoroutineWorker
If you use a CoroutineWorker
, you must implement getForegroundInfo()
. You then pass it to setForeground()
within doWork()
. Doing so will create the notification in versions of Android prior to 12.
Рассмотрим следующий пример:
class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters):
CoroutineWorker(appContext, workerParams) {
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(
NOTIFICATION_ID, createNotification()
)
}
override suspend fun doWork(): Result {
TODO()
}
private fun createNotification() : Notification {
TODO()
}
}
Политика квотирования
Вы можете контролировать, что происходит с ускоренной работой, когда ваше приложение достигает квоты выполнения. Чтобы продолжить, вы можете передать setExpedited()
:
-
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST
, что приводит к выполнению задания как обычного запроса на работу. Это показано в предыдущем фрагменте кода. -
OutOfQuotaPolicy.DROP_WORK_REQUEST
, что приводит к отмене запроса, если квоты недостаточно.
Отложенная ускоренная работа
Система старается выполнить заданное ускоренное задание как можно скорее после его вызова. Однако, как и в случае с другими типами заданий, система может отложить начало нового ускоренного задания, например, в следующих случаях:
- Нагрузка : Нагрузка на систему слишком высока. Это может произойти, если запущено слишком много заданий или в системе недостаточно памяти.
- Квота : Превышен лимит квоты для ускоренного задания. Ускоренное задание использует систему квот, основанную на App Standby Buckets, и ограничивает максимальное время выполнения в течение скользящего временного окна. Квоты, используемые для ускоренного задания, более строгие, чем квоты для других типов фоновых заданий.
График периодических работ
Время от времени вашему приложению может потребоваться периодическое выполнение определённых задач. Например, вам может потребоваться периодически создавать резервные копии данных, загружать новый контент в приложение или загружать журналы на сервер.
Вот как использовать PeriodicWorkRequest
для создания объекта WorkRequest
, который выполняется периодически:
Котлин
val saveRequest =
PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
// Additional configuration
.build()
Ява
PeriodicWorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
// Constraints
.build();
В этом примере работа запланирована с интервалом в один час.
Интервал определяется как минимальное время между повторениями. Точное время выполнения рабочего процесса зависит от ограничений, используемых в объекте WorkRequest, и от оптимизаций, выполняемых системой.
Гибкие интервалы бега
Если характер вашей работы требует соблюдения сроков выполнения, вы можете настроить PeriodicWorkRequest
на выполнение в течение гибкого периода внутри каждого интервального периода, как показано на рисунке 1.
Рисунок 1. На диаграмме показаны повторяющиеся интервалы с гибким периодом, в течение которого может выполняться работа.
Чтобы определить периодическую работу с гибким графиком, необходимо передать flexInterval
вместе с repeatInterval
при создании PeriodicWorkRequest
. Гибкий период начинается с repeatInterval - flexInterval
и продолжается до конца интервала.
Ниже приведен пример периодической работы, которая может выполняться в течение последних 15 минут каждого часового периода.
Котлин
val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
1, TimeUnit.HOURS, // repeatInterval (the period cycle)
15, TimeUnit.MINUTES) // flexInterval
.build()
Ява
WorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
1, TimeUnit.HOURS,
15, TimeUnit.MINUTES)
.build();
Интервал повторения должен быть больше или равен PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS
, а гибкий интервал должен быть больше или равен PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS` .
Влияние ограничений на периодическую работу
Вы можете применять ограничения к периодическим заданиям. Например, можно добавить ограничение к запросу на выполнение задания, чтобы задание выполнялось только во время зарядки устройства пользователя. В этом случае, даже если заданный интервал повторения пройден, PeriodicWorkRequest
не будет запущен, пока не будет выполнено это условие. Это может привести к задержке или даже пропуску конкретного выполнения задания, если условия не будут выполнены в течение интервала выполнения.
Ограничения в работе
Constraints
обеспечивают отсрочку работ до достижения оптимальных условий. WorkManager доступны следующие ограничения:
Тип сети | Ограничивает тип сети , необходимый для работы. Например, Wi-Fi ( UNMETERED ). |
BatteryNotLow | Если установлено значение true, ваша работа не будет выполняться, если устройство находится в режиме низкого заряда батареи. |
ТребуетсяЗарядка | Если установлено значение true, работа будет выполняться только во время зарядки устройства. |
Устройство в режиме ожидания | Если установлено значение true, перед запуском задания устройство пользователя должно находиться в состоянии бездействия. Это может быть полезно для запуска пакетных операций, которые в противном случае могли бы негативно повлиять на производительность других приложений, активно работающих на устройстве пользователя. |
StorageNotLow | Если установлено значение true, ваша работа не будет запущена, если на устройстве пользователя слишком мало свободного места. |
Чтобы создать набор ограничений и связать его с какой-либо работой, создайте экземпляр Constraints
с помощью Constraints.Builder()
и назначьте его своему WorkRequest.Builder()
.
Например, следующий код создает запрос на работу, который выполняется только тогда, когда устройство пользователя заряжается и подключено к сети Wi-Fi:
Котлин
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val myWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
.setConstraints(constraints)
.build()
Ява
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build();
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setConstraints(constraints)
.build();
Если указано несколько ограничений, ваша работа будет запущена только при соблюдении всех ограничений.
Если во время выполнения задания ограничение не будет выполнено, WorkManager остановит ваш обработчик. После выполнения всех ограничений задание будет запущено повторно.
Отложенная работа
Если у вашей работы нет ограничений или все ограничения соблюдены на момент постановки работы в очередь, система может решить запустить её немедленно. Если вы не хотите, чтобы работа выполнялась немедленно, вы можете указать, что она запустится с минимальной начальной задержкой.
Вот пример того, как настроить выполнение задания не менее чем через 10 минут после его постановки в очередь.
Котлин
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
Ява
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setInitialDelay(10, TimeUnit.MINUTES)
.build();
Хотя этот пример иллюстрирует установку начальной задержки для OneTimeWorkRequest
, вы также можете установить начальную задержку для PeriodicWorkRequest
. В этом случае будет задержан только первый запуск периодической работы.
Политика повторных попыток и отсрочек
Если вам необходимо, чтобы WorkManager повторил вашу задачу, вы можете вернуть Result.retry()
из вашего воркера. После этого ваша задача будет перенесена в соответствии с задержкой и политикой отсрочки .
Задержка отсрочки определяет минимальное время ожидания перед повторной попыткой выполнения после первой попытки. Это значение не может быть меньше 10 секунд (или MIN_BACKOFF_MILLIS ).
Политика отсрочки определяет, как должна увеличиваться задержка отсрочки для последующих попыток повтора. WorkManager поддерживает две политики отсрочки:
LINEAR
иEXPONENTIAL
.
Для каждого рабочего запроса предусмотрена политика отсрочки и задержка. Политика по умолчанию — EXPONENTIAL
с задержкой 30 секунд, но вы можете переопределить её в настройках рабочего запроса.
Ниже приведен пример настройки задержки и политики отсрочки.
Котлин
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
Ява
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
In this example, the minimum backoff delay is set to the minimum allowed value, 10 seconds. Since the policy is LINEAR
the retry interval will increase by approximately 10 seconds with each new attempt. For example, the first run finishing with Result.retry()
will be attempted again after 10 seconds, followed by 20, 30, 40, and so on, if the work continues to return Result.retry()
after subsequent attempts. If the backoff policy were set to EXPONENTIAL
, the retry duration sequence would be closer to 20, 40, and 80.
Тег работы
Каждый запрос на работу имеет уникальный идентификатор , который может быть использован для идентификации этой работы в дальнейшем с целью ее отмены или наблюдения за ходом ее выполнения .
Если у вас есть группа логически связанных задач, вам также может быть полезно пометить эти задачи тегами. Теги позволяют работать с группой рабочих запросов одновременно.
Например, WorkManager.cancelAllWorkByTag(String)
отменяет все запросы на работу с определенным тегом, а WorkManager.getWorkInfosByTag(String)
возвращает список объектов WorkInfo, которые можно использовать для определения текущего состояния работы.
Следующий код показывает, как можно добавить тег «очистки» к вашей работе:
Котлин
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.addTag("cleanup")
.build()
Ява
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.addTag("cleanup")
.build();
Наконец, к одному запросу на выполнение работы можно добавить несколько тегов. Внутри системы эти теги хранятся в виде набора строк. Чтобы получить набор тегов, связанных с WorkRequest
можно использовать WorkInfo.getTags()
.
Из класса Worker
вы можете получить набор тегов с помощью ListenableWorker.getTags() .
Назначить входные данные
Для выполнения вашей работы могут потребоваться входные данные. Например, для работы, связанной с загрузкой изображения, может потребоваться загрузить URI изображения в качестве входных данных.
Входные значения хранятся в виде пар «ключ-значение» в объекте Data
и могут быть заданы в запросе на выполнение. WorkManager передаст входные Data
в вашу задачу при её выполнении. Класс Worker
может получить доступ к входным аргументам, вызвав метод Worker.getInputData()
. В следующем коде показано, как создать экземпляр Worker
, требующий входных данных, и как отправить их в запросе на выполнение.
Котлин
// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
: Worker(appContext, workerParams) {
override fun doWork(): Result {
val imageUriInput =
inputData.getString("IMAGE_URI") ?: return Result.failure()
uploadFile(imageUriInput)
return Result.success()
}
...
}
// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
.setInputData(workDataOf(
"IMAGE_URI" to "http://..."
))
.build()
Ява
// Define the Worker requiring input
public class UploadWork extends Worker {
public UploadWork(Context appContext, WorkerParameters workerParams) {
super(appContext, workerParams);
}
@NonNull
@Override
public Result doWork() {
String imageUriInput = getInputData().getString("IMAGE_URI");
if(imageUriInput == null) {
return Result.failure();
}
uploadFile(imageUriInput);
return Result.success();
}
...
}
// Create a WorkRequest for your Worker and sending it input
WorkRequest myUploadWork =
new OneTimeWorkRequest.Builder(UploadWork.class)
.setInputData(
new Data.Builder()
.putString("IMAGE_URI", "http://...")
.build()
)
.build();
Аналогично, класс Data
может использоваться для вывода возвращаемого значения. Входные и выходные данные более подробно рассматриваются в разделе «Входные параметры и возвращаемые значения» .
Следующие шаги
На странице «Состояния и наблюдение» вы узнаете больше о состояниях работы и о том, как отслеживать ход ее выполнения.