В руководстве по началу работы было рассказано, как создать 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 выполнять важную работу, предоставляя системе лучший контроль над доступом к ресурсам.
Ускоренная работа отличается следующими характеристиками:
- Важность : ускоренная работа подходит для задач, которые важны для пользователя или инициированы пользователем.
- Скорость : ускоренная работа лучше всего подходит для коротких задач, которые начинаются немедленно и завершаются в течение нескольких минут.
- Квоты : Системная квота, ограничивающая время выполнения на переднем плане, определяет, может ли запуститься ускоренное задание.
- Управление питанием : ограничения управления питанием , такие как экономия заряда батареи и режим 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
Если вы используете CoroutineWorker
, вы должны реализовать getForegroundInfo()
. Затем вы передаете его в setForeground()
внутри doWork()
. Это создаст уведомление в версиях Android до 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, это требует, чтобы устройство пользователя было в режиме ожидания перед запуском работы. Это может быть полезно для запуска пакетных операций, которые в противном случае могли бы оказать негативное влияние на производительность других приложений, активно работающих на устройстве пользователя. |
ХранилищеNotLow | Если задано значение 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 поддерживает 2 политики отсрочки:
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();
В этом примере минимальная задержка отката установлена на минимально допустимое значение, 10 секунд. Поскольку политика LINEAR
, интервал повтора будет увеличиваться примерно на 10 секунд с каждой новой попыткой. Например, первый запуск, завершающийся Result.retry()
, будет повторен через 10 секунд, за которым последуют 20, 30, 40 и т. д., если работа продолжит возвращать Result.retry()
после последующих попыток. Если бы политика отката была установлена на EXPONENTIAL
, последовательность продолжительности повтора была бы ближе к 20, 40 и 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
может использоваться для вывода возвращаемого значения. Входные и выходные данные более подробно рассматриваются в разделе входные параметры и возвращаемые значения .
Следующие шаги
На странице «Состояния и наблюдение» вы узнаете больше о состояниях работы и о том, как отслеживать ход ее выполнения.