Управление работой

После того как вы определили свой Worker и свой WorkRequest , последним шагом будет постановка вашей работы в очередь. Самый простой способ поставить работу в очередь — вызвать метод enqueue() WorkManager, передав WorkRequest , который вы хотите запустить.

Котлин

val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

Ява

WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

Будьте осторожны при постановке работы в очередь, чтобы избежать дублирования. Например, приложение может пытаться загружать свои журналы во внутреннюю службу каждые 24 часа. Если вы не будете осторожны, вы можете поставить одну и ту же задачу в очередь много раз, даже если задание нужно запустить только один раз. Для достижения этой цели вы можете запланировать работу как уникальную работу .

Уникальная работа

Уникальная работа — это мощная концепция, которая гарантирует, что у вас одновременно будет только один экземпляр работы с определенным именем . В отличие от идентификаторов, уникальные имена удобочитаемы и указываются разработчиком, а не генерируются автоматически WorkManager. В отличие от тегов , уникальные имена связаны только с одним экземпляром работы.

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

Оба эти метода принимают 3 аргумента:

  • uniqueWorkNameString , используемая для уникальной идентификации рабочего запроса.
  • existingWorkPolicyenum , которое сообщает WorkManager, что делать, если уже существует незавершенная цепочка работ с этим уникальным именем. Дополнительную информацию см. в политике разрешения конфликтов .
  • workWorkRequest для планирования.

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

Котлин

val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

Ява

PeriodicWorkRequest sendLogsWorkRequest = new
      PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
              .setConstraints(new Constraints.Builder()
              .setRequiresCharging(true)
          .build()
      )
     .build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

Теперь, если код выполняется, когда задание sendLogs уже находится в очереди, существующее задание сохраняется и новое задание не добавляется.

Уникальные рабочие последовательности также могут быть полезны, если вам нужно постепенно выстраивать длинную цепочку задач. Например, приложение для редактирования фотографий может позволить пользователям отменить длинную цепочку действий. Каждая из этих операций отмены может занять некоторое время, но их необходимо выполнять в правильном порядке. В этом случае приложение может создать цепочку «отмены» и при необходимости добавлять в нее каждую операцию отмены. Дополнительные сведения см. в разделе Работа с цепочкой .

Политика разрешения конфликтов

При планировании уникальной работы вы должны сообщить WorkManager, какое действие следует предпринять в случае возникновения конфликта. Вы делаете это, передавая перечисление при постановке работы в очередь.

Для разовой работы вы предоставляете ExistingWorkPolicy , который поддерживает 4 варианта обработки конфликта.

  • REPLACE существующую работу новой. Эта опция отменяет существующую работу.
  • KEEP существующую работу и игнорируйте новую работу.
  • APPEND новую работу в конец существующей работы. Эта политика приведет к тому, что ваша новая работа будет привязана к существующей работе и будет запущена после завершения существующей работы.

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

  • APPEND_OR_REPLACE функционирует аналогично APPEND , за исключением того, что он не зависит от предварительного рабочего статуса. Если существующая работа CANCELLED или FAILED , новая работа все равно выполняется.

Для периодической работы вы предоставляете ExistingPeriodicWorkPolicy , который поддерживает 2 параметра: REPLACE и KEEP . Эти параметры функционируют так же, как и их аналоги ExistingWorkPolicy.

Наблюдение за вашей работой

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

Котлин

// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

Ява

// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>

Запрос возвращает ListenableFuture объекта WorkInfo , который включает id работы, ее теги, текущее State и любые выходные данные, установленные через Result.success(outputData) .

Вариант LiveData каждого из методов позволяет наблюдать за изменениями в WorkInfo путем регистрации прослушивателя. Например, если вы хотите отображать пользователю сообщение об успешном завершении какой-либо работы, вы можете настроить его следующим образом:

Котлин

workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { workInfo ->
   if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
       Snackbar.make(requireView(), 
      R.string.work_completed, Snackbar.LENGTH_SHORT)
           .show()
   }
}

Ява

workManager.getWorkInfoByIdLiveData(syncWorker.id)
        .observe(getViewLifecycleOwner(), workInfo -> {
    if (workInfo.getState() != null &&
            workInfo.getState() == WorkInfo.State.SUCCEEDED) {
        Snackbar.make(requireView(),
                    R.string.work_completed, Snackbar.LENGTH_SHORT)
                .show();
   }
});

Сложные рабочие запросы

WorkManager 2.4.0 и более поздние версии поддерживают сложные запросы к заданиям в очереди с использованием объектов WorkQuery . WorkQuery поддерживает запросы на работу по сочетанию тегов, состояния и уникального имени работы.

В следующем примере показано, как можно найти все работы с тегом «syncTag» , который находится в состоянии FAILED или CANCELLED и имеет уникальное рабочее имя « preProcess » или « sync ».

Котлин

val workQuery = WorkQuery.Builder
       .fromTags(listOf("syncTag"))
       .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(listOf("preProcess", "sync")
    )
   .build()

val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)

Ява

WorkQuery workQuery = WorkQuery.Builder
       .fromTags(Arrays.asList("syncTag"))
       .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
     )
    .build();

ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);

Каждый компонент (тег, состояние или имя) в WorkQuery объединяется AND с остальными. Каждое значение в компоненте имеет операцию OR . Например: (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...) .

WorkQuery также работает с эквивалентом LiveData — getWorkInfosLiveData() .

Отмена и остановка работы

Если вам больше не требуется выполнение ранее поставленной в очередь работы, вы можете запросить ее отмену. Работу можно отменить по ее name , id или связанному с ней tag .

Котлин

// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")

Ява

// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

Под капотом WorkManager проверяет State работы. Если работа уже завершена , ничего не происходит. В противном случае состояние работы изменится на CANCELLED , и работа не будет выполняться в будущем. Любые задания WorkRequest , зависящие от этой работы, также будут CANCELLED .

В настоящее время RUNNING работа получает вызов ListenableWorker.onStopped() . Переопределите этот метод для обработки любой потенциальной очистки. Дополнительную информацию см. в разделе «Остановка работающего работника» .

Остановить работающего работника

Существует несколько разных причин, по которым ваш работающий Worker может быть остановлен WorkManager:

  • Вы явно запросили его отмену (например, вызвав WorkManager.cancelWorkById(UUID) ).
  • В случае уникальной работы вы явно ставите в очередь новый WorkRequest с ExistingWorkPolicy , равным REPLACE . Старый WorkRequest немедленно считается отмененным.
  • Ограничения вашей работы больше не удовлетворяются.
  • Система по какой-то причине приказала вашему приложению прекратить работу. Это может произойти, если вы превысите срок исполнения в 10 минут. Работу планируется повторить позднее.

В этих условиях ваш Worker остановлен.

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

обратный вызов onStopped()

WorkManager вызывает ListenableWorker.onStopped() как только ваш Worker был остановлен. Переопределите этот метод, чтобы закрыть все ресурсы, которые вы можете удерживать.

свойство isStopped()

Вы можете вызвать метод ListenableWorker.isStopped() чтобы проверить, был ли уже остановлен ваш рабочий процесс. Если вы выполняете длительные или повторяющиеся операции в своем Worker, вам следует часто проверять это свойство и использовать его как сигнал для скорейшего прекращения работы.

Примечание. WorkManager игнорирует набор Result Worker, получившего сигнал onStop, поскольку Worker уже считается остановленным.