Определите рабочие запросы

В руководстве по началу работы описано, как создать простой 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 выполнять важную работу, одновременно предоставляя системе лучший контроль над доступом к ресурсам.

Ускоренная работа отличается следующими характеристиками:

  • Важность : ускоренная работа подходит для задач, которые важны для пользователя или инициированы пользователем.
  • Скорость : ускоренная работа лучше всего подходит для коротких задач, которые начинаются немедленно и выполняются в течение нескольких минут.
  • Квоты . Квота на уровне системы, ограничивающая время приоритетного выполнения, определяет, может ли быть запущено ускоренное задание.
  • Управление питанием . Ограничения управления питанием , такие как «Экономия заряда» и «Режим сна», с меньшей вероятностью повлияют на ускорение работы.
  • Задержка : система немедленно выполняет ускоренную работу при условии, что текущая рабочая нагрузка системы позволяет это сделать. Это означает, что они чувствительны к задержке и не могут быть запланированы для последующего выполнения.

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

Квоты

Система должна выделить время выполнения ускоренного задания, прежде чем оно сможет запуститься. Срок исполнения не неограничен. Скорее, каждое приложение получает квоту времени выполнения. Когда ваше приложение использует свое время выполнения и достигает выделенной квоты, вы больше не можете выполнять ускоренную работу до тех пор, пока квота не обновится. Это позволяет Android более эффективно балансировать ресурсы между приложениями.

Количество времени выполнения, доступное приложению, зависит от резервного сегмента и важности процесса.

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

Выполнение срочной работы

Начиная с WorkManager 2.7, ваше приложение может вызывать setExpedited() чтобы объявить, что WorkRequest должен выполняться как можно быстрее с использованием ускоренного задания. Следующий фрагмент кода представляет собой пример использования setExpedited() :

Котлин

val request = OneTimeWorkRequestBuilder<SyncWorker>()
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build()

WorkManager.getInstance(context)
    .enqueue(request)

Ява

OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
    .setInputData(inputData)
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .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 , который приводит к отмене запроса, если квоты недостаточно.

Пример приложения

Чтобы увидеть полный пример того, как WorkManager 2.7.0 использует ускоренную работу, просмотрите WorkManagerSample на GitHub.

Отложенная ускоренная работа

Система пытается выполнить данное ускоренное задание как можно скорее после его вызова. Однако, как и в случае с другими типами заданий, система может отложить начало новой ускоренной работы, например, в следующих случаях:

  • Нагрузка : загрузка системы слишком высока, что может произойти, если уже запущено слишком много заданий или в системе недостаточно памяти.
  • Квота : превышен лимит квоты ускоренных заданий. Ускоренная работа использует систему квот, основанную на сегментах ожидания приложений и ограничивающую максимальное время выполнения в пределах скользящего временного окна. Квоты, используемые для ускоренной работы, более строгие, чем те, которые используются для других типов фоновых задач.

График периодической работы

Иногда вашему приложению может потребоваться периодическое выполнение определенной работы. Например, вы можете периодически создавать резервные копии своих данных, загружать свежий контент в свое приложение или загружать журналы на сервер.

Вот как вы используете 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.

Вы можете установить гибкий интервал для периодического задания. Вы определяете интервал повторения и гибкий интервал, который определяет определенное количество времени в конце интервала повторения. WorkManager пытается запустить ваше задание в определенный момент гибкого интервала каждого цикла.

Рисунок 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 не будет запущен до тех пор, пока не будет выполнено это условие. Это может привести к задержке или даже пропуску определенного выполнения вашей работы, если условия не будут выполнены в течение интервала выполнения.

Ограничения по работе

Ограничения гарантируют, что работа откладывается до тех пор, пока не будут достигнуты оптимальные условия. Следующие ограничения доступны WorkManager.

Тип сети Ограничивает тип сети, необходимой для работы вашей работы. Например, Wi-Fi ( UNMETERED ).
БатареяNotLow Если установлено значение true, ваша работа не будет работать, если устройство находится в режиме низкого заряда батареи.
Требует зарядки Если установлено значение true, ваша работа будет выполняться только во время зарядки устройства.
Устройство в режиме ожидания Если установлено значение true, перед запуском работы устройство пользователя должно находиться в режиме ожидания. Это может быть полезно для выполнения пакетных операций, которые в противном случае могли бы отрицательно повлиять на производительность других приложений, активно работающих на устройстве пользователя.
ХранениеNotLow Если установлено значение true, ваша работа не будет выполняться, если на устройстве пользователя слишком мало места для хранения данных.

Чтобы создать набор ограничений и связать его с некоторой работой, создайте экземпляр Constraints с помощью Contraints.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();

В этом примере минимальная задержка отсрочки установлена ​​на минимально допустимое значение, 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 можно использовать для вывода возвращаемого значения. Входные и выходные данные более подробно рассмотрены в разделе «Входные параметры и возвращаемые значения» .

Следующие шаги

На странице «Состояния и наблюдение» вы узнаете больше о рабочих состояниях и о том, как отслеживать ход своей работы.