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

В руководстве по началу работы было описано, как создать запрос WorkRequest и поставить его в очередь.

В этом руководстве вы узнаете, как определять и настраивать объекты WorkRequest для обработки распространенных сценариев использования, например, как:

  • Планируйте разовые и регулярные работы.
  • Установите ограничения на работу, например, требование наличия Wi-Fi или возможности зарядки.
  • Гарантировать минимальную задержку в выполнении работ.
  • Настройте стратегии повторных попыток и задержки.
  • Передайте входные данные для выполнения работы.
  • Совместная работа над смежными задачами с использованием тегов.

Обзор

В WorkManager задача определяется с помощью WorkRequest . Для планирования любой задачи в WorkManager необходимо сначала создать объект WorkRequest , а затем добавить его в очередь.

Котлин

val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)

Java

WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);

Объект WorkRequest содержит всю информацию, необходимую WorkManager для планирования и выполнения вашей работы. Он включает в себя ограничения, которые должны быть соблюдены для выполнения вашей работы, информацию о планировании, такую ​​как задержки или повторяющиеся интервалы, конфигурацию повторных попыток, а также может включать входные данные, если ваша работа зависит от них.

Сам WorkRequest является абстрактным базовым классом. Существуют две производные реализации этого класса, которые можно использовать для создания запроса: OneTimeWorkRequest и PeriodicWorkRequest . Как следует из их названий, OneTimeWorkRequest полезен для планирования неповторяющихся работ, в то время как PeriodicWorkRequest больше подходит для планирования работ, которые повторяются с определенным интервалом.

Запланируйте разовую работу

Для выполнения простых задач, не требующих дополнительной настройки, используйте статический метод from :

Котлин

val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)

Java

WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);

Для более сложных задач можно использовать конструктор:

Котлин

val uploadWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       // Additional configuration
       .build()

Java

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)

Java

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()

Java

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()

Java

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 ).
Заряд батареи не низкий Если установить значение true, ваша работа не будет выполняться, если устройство находится в режиме низкого заряда батареи.
Требуется зарядка Если установить значение true, ваша работа будет выполняться только во время зарядки устройства.
DeviceIdle Если установить значение 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()

Java

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()

Java

WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();

В примере показано, как установить начальную задержку для OneTimeWorkRequest , но вы также можете установить начальную задержку для PeriodicWorkRequest . В этом случае будет отложен только первый запуск вашей периодической работы.

политика повторных попыток и отката

Если вам необходимо, чтобы WorkManager повторил выполнение вашей задачи, вы можете вернуть Result.retry() из вашего обработчика. В этом случае ваша задача будет перепланирована в соответствии с задержкой и политикой отсрочки .

  • Параметр Backoff delay задает минимальное время ожидания перед повторной попыткой выполнения работы после первой попытки. Это значение не может быть меньше 10 секунд (или MIN_BACKOFF_MILLIS ).

  • Политика задержки определяет, как должна увеличиваться задержка при последующих попытках повторного выполнения. WorkManager поддерживает 2 политики задержки: LINEAR и EXPONENTIAL .

Для каждого запроса на выполнение работы установлен режим и задержка обработки. По умолчанию используется EXPONENTIAL режим с задержкой в ​​30 секунд, но вы можете изменить это в конфигурации запроса на выполнение работы.

Вот пример настройки задержки и политики отсрочки.

Котлин

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setBackoffCriteria(
       BackoffPolicy.LINEAR,
       WorkRequest.MIN_BACKOFF_MILLIS,
       TimeUnit.MILLISECONDS)
   .build()

Java

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setBackoffCriteria(
                       BackoffPolicy.LINEAR,
                       WorkRequest.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, которые можно использовать для определения текущего состояния работы.

Следующий код показывает, как добавить тег "cleanup" к вашей работе:

Котлин

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .addTag("cleanup")
   .build()

Java

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()

Java

// 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 для вывода возвращаемого значения.

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

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