Я столкнулся с поведением WorkManager
(версия 2.0.1
), которое не могу понять.К сожалению, такое поведение приводит к проблемам в моем приложении.Чтобы проиллюстрировать мою проблему, я буду использовать более простой пример.
Предположим, у нас есть три Worker
реализации - UniqueWorker1
, UniqueWorker2
и FinishingWorker
.
class UniqueWorker1(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun doWork(): Result {
if (runAttemptCount == 0) {
Log.d("UniqueWorker1", "First try.")
return Result.retry()
}
Log.d("UniqueWorker1", "Second try")
return Result.success()
}
}
class UniqueWorker2(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun doWork(): Result {
Log.d("UniqueWorker2", "doWork")
return Result.success()
}
}
class FinishingWorker(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun doWork(): Result {
Log.d("FinishingWorker", "doWork")
return Result.success()
}
}
КакВы можете видеть, что первый работник успешен после второй попытки запуска.Другие просто регистрируют сообщение и возвращают успешный результат.
Теперь я ставлю этих рабочих в двух направлениях.Сначала я запускаю UniqueWorker1
как уникальную работу и приказываю WorkManager
запустить FinishingWorker
, когда UniqueWorker1
завершается успешно.
val uniqueWorker1 = OneTimeWorkRequest.Builder(UniqueWorker1::class.java).build()
val finishingWorker = OneTimeWorkRequest.Builder(FinishingWorker::class.java).build()
val uniqueWorkContinuation = WorkManager.getInstance()
.beginUniqueWork("UniqueWorker", ExistingWorkPolicy.KEEP, uniqueWorker1)
val continuations = listOf(uniqueWorkContinuation)
WorkContinuation.combine(continuations)
.then(finishingWorker)
.enqueue()
Второй способ выглядит следующим образом: я объединяю уникальные работы UniqueWork1
и UniqueWork2
.Затем я говорю WorkManager
запустить FinishingWorker
, когда обе работы завершены.
val uniqueWorker1 = OneTimeWorkRequest.Builder(UniqueWorker1::class.java).build()
val uniqueWorker2 = OneTimeWorkRequest.Builder(UniqueWorker2::class.java).build()
val finishingWorker = OneTimeWorkRequest.Builder(FinishingWorker::class.java).build()
val uniqueWorkContinuation1 = WorkManager.getInstance()
.beginUniqueWork("UniqueWorker1", ExistingWorkPolicy.KEEP, uniqueWorker1)
val uniqueWorkContinuation2 = WorkManager.getInstance()
.beginUniqueWork("UniqueWorker2", ExistingWorkPolicy.KEEP, uniqueWorker2)
val continuations = listOf(uniqueWorkContinuation1, uniqueWorkContinuation2)
WorkContinuation.combine(continuations)
.then(finishingWorker)
.enqueue()
Теперь представьте себе такой случай.Я начинаю рабочих в первую очередь.UniqueWorker1
повторяется, потому что это его первая попытка запуска.У нас есть 30 секунд ожидания (со значениями по умолчанию BackoffPolicy
).Прежде чем он попытается, я начинаю рабочих вторым способом.UniqueWorker1
не ставится в очередь (потому что он уже запущен), но UniqueWorker2
начинает свою работу.Теперь через 30 секунд UniqueWorker1
завершается успешно, WorkManager
запускает FinishingWorker
из-за комбинации первого способа работы.Проблема в том, что WorkManager
не запускается FinishingWorker
во второй раз.Почему он должен начать FinishingWorker
во второй раз?Потому что комбинация работы вторым способом указывает начинать FinishingWorker
, когда UniqueWorker1
успешно и UniqueWorker2
успешно.UniqueWorker2
удалось сразу, а UniqueWorker1
удалось через 30 с.
Вначале я думал, что когда WorkerManager
видит, что когда одна из работ в комбинации работ уже поставлена в очередь, она не закончится и не выиграетне выполняется запрос из then
метода.Но я проверил это в более простом примере, и это сработало.
Итак, вывод описанной мной ситуации выглядит следующим образом:
// Run workers in a first way
D/UniqueWorker1: First try.
I/WM-WorkerWrapper: Worker result RETRY for Work [ id=7e2fe6b4-4c8e-42af-8a13-244c0cc30059, tags={ UniqueWorker1 } ]
// Run workers in a second way before 30s will pass
E/WM-EnqueueRunnable: Prerequisite b98a6246-28d4-4b25-ae50-ec3dda6cd3ac doesn't exist; not enqueuing
E/WM-EnqueueRunnable: Prerequisite 02d017e7-30b0-4038-9b44-a6217da3979c doesn't exist; not enqueuing
D/UniqueWorker2: doWork
I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=ce9810cd-9565-4cad-b7d1-9556a01eae67, tags={ UniqueWorker2 } ]
// 30s passed
D/UniqueWorker1: Second try
I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=7e2fe6b4-4c8e-42af-8a13-244c0cc30059, tags={ UniqueWorker1 } ]
I/WM-WorkerWrapper: Setting status to enqueued for c2ac89de-3a67-496f-93e6-037d85d11646
I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=c2ac89de-3a67-496f-93e6-037d85d11646, tags={ androidx.work.impl.workers.CombineContinuationsWorker } ]
I/WM-WorkerWrapper: Setting status to enqueued for 3287bbec-b1c4-488a-b64b-35e0e6b58137
D/FinishingWorker: doWork
I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=3287bbec-b1c4-488a-b64b-35e0e6b58137, tags={ FinishingWorker } ]
Как видите, FinishingWorker
был поставлен в очередь только один раз,Извините за длинное объяснение, но этот пример показывает именно мою проблему.Это серьезная проблема для меня, потому что некоторые важные работники не поставлены в очередь.
Вопрос
Может кто-нибудь объяснить причину такого поведения?Это предполагаемое поведение WorkManager
или это ошибка?