android - 组合 WorkContinuation 没有按预期工作

标签 android kotlin android-jetpack android-workmanager

我遇到了我无法理解的 WorkManager(版本 2.0.1)的行为。不幸的是,这种行为会导致我的应用程序出现问题。为了说明我的问题,我将使用一个更简单的示例。

假设有三个 Worker 实现 - UniqueWorker1UniqueWorker2FinishingWorker

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

如您所见,第一个工作人员在第二次运行尝试后成功。其他人只是记录消息并返回成功的结果。

现在我以两种方式对这些 worker 进行排队。首先,我将 UniqueWorker1 作为唯一工作启动,并告诉 WorkManagerUniqueWorker1 成功时运行 FinishingWorker

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

第二种方式是这样的:我结合了 UniqueWork1UniqueWork2 的独特作品。然后我告诉 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()

现在想象一下这样的情况。我以第一种方式启动 worker 。 UniqueWorker1 重试,因为这是他第一次尝试运行。我们有 30 秒的等待时间(使用默认的 BackoffPolicy 值)。在重试之前,我以第二种方式启动 worker 。 UniqueWorker1 没有入队(因为它已经启动)但 UniqueWorker2 开始工作。现在 30 秒后,UniqueWorker1 成功,WorkManager 启动 FinishingWorker,因为第一种工作组合方式。问题是 WorkManager 没有第二次启动 FinishingWorker。为什么要第二次启动 FinishingWorker?因为第二种方式的工作组合告诉您在 UniqueWorker1 成功且 UniqueWorker2 成功时启动 FinishingWorkerUniqueWorker2 立即成功,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 的预期行为还是错误?

最佳答案

我认为您在处理独特工作的方式上存在一些困惑,因此很难推断出您在做什么。

你有以下序列:

  1. “UniqueWorker”:UniqueWorker1 -> FinishingWorker
  2. “UniqueWorker1”:UniqueWorker1
  3. “UniqueWorker2”:UniqueWorker2
  4. (未命名):结合(UniqueWorker1,UniqueWorker2)-> FinishingWorker

首先,序列 1 与您的其余代码无关。它有自己的名字;它与其余部分无关。它将尝试独立执行,并在返回成功时第二次尝试成功,然后执行 FinishingWorker。

那么让我们继续讨论剩下的部分。如果您连续两次运行第二种方法(甚至没有对第一个序列进行排队),此代码将显示错误消息“先决条件 [something] 不存在;未排队”。那是因为您尝试使用 ExistingWorkPolicy.KEEP 将 UniqueWorkers 入队两次。第二次,该政策将生效并且不会加入任何新内容。那时,当您决定将 FinishingWorker 加入队列时,它将没有 parent 。因此,此行为按预期工作。

您似乎对独特作品的运作方式有些困惑。我认为对你来说简短的回答是每个逻辑工作分组都应该有相同的唯一名称。否则你会遇到这样奇怪的问题。例如,您可以将第二种方法重写为如下所示:

WorkManager.getInstance(context)
  .beginUniqueWork("second_method_name", KEEP, listOf(uniqueWork1, uniqueWork2))
  .then(finishingWork)
  .enqueue()

我建议在这里进一步阅读:https://developer.android.com/topic/libraries/architecture/workmanager/how-to/unique-work以及 API 文档:

public abstract WorkContinuation beginUniqueWork (String uniqueWorkName, 
                ExistingWorkPolicy existingWorkPolicy, 
                List<OneTimeWorkRequest> work)

关于android - 组合 WorkContinuation 没有按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56815862/

相关文章:

JetPack Jetifier 的 Android Crashlytics 问题

android - Jetpack Compose 在 Image 组件上添加可点击修饰符会更改布局

java - Kotlin + Java 9 模块(使用 Java 11)+ maven

android - 多个数据源的分页库DataSource.Factory

android - Android 中的基本线程

android - 对话框 Activity - 添加按钮点击监听器

kotlin - 在流的收集中获取当前和以前的值

java - 如何使用 Intent 设置选择图像的限制

android - Android 如何处理未知的 XML 标签?

php - 使用 Android Developer、PHP 和 MySQL 的 Android 应用出现错误 : org. json.JSONException:状态无值