WorkManager:为什么失败的独特工作无法使用“ APPEND” ExistingWork策略进行同名工作?

时间:2018-10-28 16:45:31

标签: android android-jetpack android-workmanager

假设我们正在开发一个消息传递应用程序,我们希望将消息发送到给定的对话中,这些消息的顺序仅在该对话中很重要,并且如果该应用程序置于后台,我们希望保证该消息将被发送。

为此,WorkManager#beginUniqueWork方法似乎是理想的选择,其中uniqueWorkName将是一些对话ID,而ExistingWorkPolicy.APPEND将被用作工作策略,以按计划的顺序保持工作。

到目前为止,在我的应用程序中,只要每一项工作都返回Result.SUCCESS,那么将来任何计划的工作都将按预期执行。但是,如果一条特定的消息无法以致命的方式发送,并且我返回了Result.FAILURE,那么以后所有使用相同对话ID的工作似乎都无法达到我的Worker#doWork()的实现。

在深入研究EnqueueRunnable类的源代码之后,这似乎是一个非常刻意的选择。我无法真正理解的是为什么呢?奇怪的是,如果uniqueWorkName失败了,那么该名称在应用程序的整个生命周期中都将变得无法使用(这种情况会在终止该应用程序的过程中持续存在)。

此外,我想知道是否有人对此有一个好的解决方案,或者想知道这在WorkManager的未来版本中是否会改变。到目前为止,我能想到的最好的办法是返回Result.SUCCESS,但在输出Data中编码我自己的失败状态,以便工作的任何观察者都知道它已经失败了。但是,这有点尴尬,对于以后的代码维护者来说不是很明显(并且在查看给定Work的日志时可能会有些混乱)。

也许我原本打算使用独特作品的想法是完全错误的,并且有更好的解决方案。任何想法将不胜感激,谢谢!

3 个答案:

答案 0 :(得分:4)

因此我在this google issue tracker report中找到了自己问题的答案。

基本上,使用APPEND策略进行的独特工作会创建一个WorkContinuation,每个新项目都被链接在一起,就好像我们要使用WorkContinuation#then方法一样。失败或取消链后,将取消所有下游工作,因此这是预期的行为。

该票证建议使用2种方法:

  

如果您确实想要APPEND的行为,那么您可以做的另一件事是检查WorkRequests的WorkStatuses,并且(如果所有这些都被取消了)使用REPLACE而不是APPEND。请记住,这从本质上讲是合理的,因为您的WorkRequests可能尚未取消。因此,请确保在使用WorkManager的API时具有一些同步原语。

  

最简单的方法是不实际返回Result.FAILED;如果您始终返回SUCCEEDED并在输出数据中返回实际的通过/失败位(如果需要),则可以确保链始终保持运行状态。

这就是我已经在做的。希望这对其他人有帮助。

答案 1 :(得分:3)

关于此事的重要更新: https://developer.android.com/reference/kotlin/androidx/work/ExistingWorkPolicy#append_or_replace

APPEND_OR_REPLACE

枚举APPEND_OR_REPLACE:ExistingWorkPolicy

如果 现有的待处理(未完成)工作具有相同的唯一性 名称,将新指定的作品附加为所有叶子的子代 工作顺序。否则,将新指定的工作插入为 新序列的开始。

注意:如果失败或取消 先决条件,这些先决条件被删除,并且是新指定的 工作是新序列的开始。

这很可能是团队对这个问题的回应。这个新的ExistingWorkPolicy在版本2.4.0-alpha01中可用。

进一步测试后编辑...

因此,如果由于某种原因它一次失败,那么此修复程序唯一无法重用UniqueWorkContinuation。但是,主要功能例如当许多WorkChain是具有相同唯一名称的队列时,仍然无法取消一个Workchains仍然不起作用。通常,考虑:WorkChainACompressWorkerAUploadWorkerA,然后将WorkChainBCompressWorkerBUploadWorkerB排队,它们都在同一{{1}下}。取消或失败UniqueWorkName中的任何Worker会导致WorkChainA永远无法运行...因此,我们仍然必须确保每次都返回WorkChainB。并且不要在Result.success()中使用任何cancelWorkByTagcancelWorkById

答案 2 :(得分:0)

与之堆叠在一起,感谢您对这个问题的调查。

玩了一段时间以找到最佳解决方案后,目前该解决方案对我来说有效

startUpload() {

   workManager.pruneWork()
   //Prunes all eligible finished work from the internal database.

   photoAdapter.data.forEach {
      val workRequest = OneTimeWorkRequestBuilder<FileUploadWorker>()
            .setInputData(workDataOf(IMAGE_PATH_PROP to it.path))
            .addTag(UPLOADER_TAG)
            .build()

      workManager.enqueueUniqueWork(UNIQUE_WORK_TAG, ExistingWorkPolicy.APPEND, workRequest)
   }
}

调用“ pruneWork ”可以使您忘记已取消或失败的作品。 但是,我想这种解决方案可能具有凹腔,因此请小心。

另一个想法可能像这样:

workManager.getWorkInfosForUniqueWork(UNIQUE_WORK_TAG).get().forEach {

            if ( it.state == WorkInfo.State.FAILED || it.state == WorkInfo.State.CANCELLED) {
                //Redefine your UNIQUE_WORK_TAG
                UNIQUE_WORK_TAG = UUID().toString()
                return
            }
        }

尝试查找失败的WorkInfo,然后“切换”到新的唯一工作标签。 更好的方法表示赞赏。

p.s。除了(KEEP,REPLACE,APPEND)之外,“ androidx.work:work-runtime-ktx:2.3.4”没有新的ExistingWorkPolicy