После того как вы определили Worker и WorkRequest, остается только поставить задачу в очередь. Самый простой способ поставить задачу в очередь – вызвать метод enqueue() WorkManager, передав ему WorkRequest, который вы хотите запустить.
Kotlin
val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)
Java
WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);
Чтобы избежать дублирования, будьте осторожны при постановке задач в очередь. Например, приложение может пытаться загрузить свои журналы в серверную службу каждые 24 часа. Если вы не будете осторожны, вы можете в конечном итоге поставить в очередь одну и ту же задачу много раз, даже если задание нужно запустить только один раз. Для этого можно запланировать уникальную работу.
Уникальная работа
Уникальная работа – это мощная концепция, которая гарантирует, что у вас есть только один экземпляр работы с определенным именем за раз. В отличие от идентификаторов, уникальные имена понятны человеку и указываются разработчиком, а не генерируются автоматически WorkManager. В отличие от тегов, уникальные имена связаны только с одним экземпляром работы.
Уникальная работа может быть как разовой, так и периодической. Вы можете создать уникальную последовательность задач, вызвав один из этих методов в зависимости от того, планируете ли вы повторяющуюся или однократную задачу.
WorkManager.enqueueUniqueWork()за разовую работуWorkManager.enqueueUniquePeriodicWork()для периодических задач;
Оба метода принимают три аргумента:
- uniqueWorkName –
String, используемый для уникальной идентификации запроса на работу. - existingWorkPolicy –
enum, который сообщает WorkManager, что делать, если уже есть незавершенная цепочка работ с таким уникальным названием. Подробнее о правилах разрешения конфликтов… - work –
WorkRequest, которое нужно запланировать.
Используя уникальную работу, мы можем исправить проблему с дублированием расписания, отмеченную ранее.
Kotlin
val sendLogsWorkRequest =
PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest
)
Java
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, который поддерживает четыре варианта обработки конфликта.
REPLACEсуществующих работ с новой. При выборе этого варианта текущая работа будет отменена.KEEPсуществующую работу и игнорировать новую.APPENDновую работу в конец существующей. В этом случае новая задача будет связана с существующей и начнет выполняться после ее завершения.
Существующая работа становится предварительным условием для новой. Если существующая работа становится CANCELLED или FAILED, то новая работа также становится CANCELLED или FAILED.
Если вы хотите, чтобы новая задача выполнялась независимо от статуса существующей, используйте APPEND_OR_REPLACE.
APPEND_OR_REPLACEработает аналогичноAPPEND, но не зависит от статуса предварительной работы. Если существующая задача имеет статусCANCELLEDилиFAILED, новая задача все равно будет выполнена.
Для периодических произведений указывается свойство ExistingPeriodicWorkPolicy, которое поддерживает два варианта: REPLACE и KEEP. Эти варианты работают так же, как и их аналоги ExistingWorkPolicy.
Наблюдаю за вашей работой
После постановки задачи в очередь вы можете в любой момент проверить ее статус, запросив у WorkManager name, id или связанный с ней tag.
Kotlin
// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
Java
// 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, регистрируя прослушиватель. Например, если вы хотите показать пользователю сообщение об успешном завершении работы, вы можете настроить это следующим образом:
Kotlin
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect{ workInfo ->
if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show()
}
}
Java
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.
Kotlin
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)
Java
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-ed с другими. Каждое значение в компоненте OR. Например: (name1 OR name2
OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).
WorkQuery также работает с эквивалентом LiveData,
getWorkInfosLiveData(),
и эквивалентом Flow, getWorkInfosFlow().
Отмена и прекращение работы
Если вам больше не нужно выполнять ранее поставленную в очередь работу, вы можете попросить ее отменить. Работу может отменить ее name, id или связанный с ней tag.
Kotlin
// by id
workManager.cancelWorkById(syncWorker.id)
// by name
workManager.cancelUniqueWork("sync")
// by tag
workManager.cancelAllWorkByTag("syncTag")
Java
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
WorkManager проверяет State задачи. Если работа уже завершена, ничего не произойдет. В противном случае статус задачи изменится на CANCELLED и она не будет выполняться в будущем. Все задания WorkRequest, зависящие от этой работы, также будут CANCELLED.
RUNNING work
получает вызов
ListenableWorker.onStopped().
Переопределите этот метод, чтобы выполнить очистку. Подробнее о том, как остановить работающий воркер…
Как остановить работающую рабочую ВМ
Есть несколько причин, по которым WorkManager может остановить выполнение Worker:
- Вы явно запросили отмену (например, вызвав
WorkManager.cancelWorkById(UUID)). - В случае с уникальной задачей вы явно добавили в очередь новую задачу
WorkRequestсExistingWorkPolicyREPLACE. СтараяWorkRequestсразу же считается отмененной. - Ограничения для вашей работы больше не соблюдаются.
- Система по какой-то причине дала приложению команду остановить работу. Это может произойти, если вы превысите срок выполнения в 10 минут. Повторная попытка будет выполнена позже.
В этих условиях Worker останавливается.
Вы должны совместно прервать любую выполняемую работу и освободить все ресурсы, которые удерживает ваш Worker. Например, в этот момент следует закрыть открытые дескрипторы баз данных и файлов. Есть два механизма, которые позволяют понять, когда Worker останавливается.
Обратный вызов onStopped()
WorkManager вызывает ListenableWorker.onStopped(), как только Worker останавливается. Переопределите этот метод, чтобы закрыть все ресурсы, которые вы можете удерживать.
Свойство isStopped()
Чтобы проверить, остановлен ли воркер, вызовите метод ListenableWorker.isStopped(). Если вы выполняете длительные или повторяющиеся операции в Worker, вам следует часто проверять это свойство и использовать его как сигнал для скорейшего прекращения работы.
Примечание. WorkManager игнорирует значение Result, заданное исполнителем, который получил сигнал onStop, поскольку исполнитель уже считается остановленным.