После определения 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. В отличие от тегов , уникальные имена связаны только с одним экземпляром работы.
Уникальная работа может применяться как к разовым, так и к периодическим работам. Вы можете создать уникальную последовательность работ, вызвав один из этих методов, в зависимости от того, планируете ли вы повторяющуюся или разовую работу.
-
WorkManager.enqueueUniqueWork()
для одноразовой работы -
WorkManager.enqueueUniquePeriodicWork()
для периодической работы
Оба эти метода принимают 3 аргумента:
- uniqueWorkName —
String
используемая для уникальной идентификации запроса на работу. - existingWorkPolicy —
enum
, сообщающее WorkManager, что делать, если уже существует незавершённая цепочка работ с этим уникальным именем. Подробнее см. в политике разрешения конфликтов . - work -
WorkRequest
для планирования.
Используя уникальную работу, мы можем исправить проблему дублирования расписания, о которой упоминалось ранее.
Котлин
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
, который поддерживает два параметра: 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
и Flow
каждого из методов позволяют отслеживать изменения в WorkInfo
, регистрируя прослушиватель. Например, если вы хотите отображать сообщение пользователю после успешного завершения какой-либо работы, это можно настроить следующим образом:
Котлин
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect{ 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()
и эквивалентом Flow, getWorkInfosFlow()
.
Отмена и остановка работы
Если вам больше не нужно выполнять ранее поставленную в очередь задачу, вы можете запросить её отмену. Отменить задачу можно по её 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. Например, на этом этапе следует закрыть открытые дескрипторы баз данных и файлов. Существует два механизма, позволяющих определить, останавливается ли ваш Worker.
обратный вызов onStopped()
WorkManager вызывает метод ListenableWorker.onStopped()
сразу после остановки вашего Worker. Переопределите этот метод, чтобы закрыть все ресурсы, которые вы могли удерживать.
свойство isStopped()
Вы можете вызвать метод ListenableWorker.isStopped()
, чтобы проверить, остановлен ли ваш воркер. Если вы выполняете в воркере длительные или повторяющиеся операции, следует регулярно проверять это свойство и использовать его как сигнал для скорейшей остановки работы.
Примечание: WorkManager игнорирует Result
, установленный Worker, получившим сигнал onStop, поскольку Worker уже считается остановленным.
Наблюдать за состоянием причины остановки
Чтобы отладить причину остановки Worker
, вы можете зарегистрировать причину остановки, вызвав WorkInfo.getStopReason()
:
Котлин
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect { workInfo ->
if (workInfo != null) {
val stopReason = workInfo.stopReason
logStopReason(syncWorker.id, stopReason)
}
}
Ява
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo != null) {
int stopReason = workInfo.getStopReason();
logStopReason(syncWorker.id, workInfo.getStopReason());
}
});