假设我们正在开发一个消息传递应用程序,我们希望将消息发送到给定的对话中,这些消息的顺序仅在该对话中很重要,并且如果该应用程序置于后台,我们希望保证该消息将被发送。
为此,WorkManager#beginUniqueWork
方法似乎是理想的选择,其中uniqueWorkName
将是一些对话ID,而ExistingWorkPolicy.APPEND
将被用作工作策略,以按计划的顺序保持工作。
到目前为止,在我的应用程序中,只要每一项工作都返回Result.SUCCESS
,那么将来任何计划的工作都将按预期执行。但是,如果一条特定的消息无法以致命的方式发送,并且我返回了Result.FAILURE
,那么以后所有使用相同对话ID的工作似乎都无法达到我的Worker#doWork()
的实现。
在深入研究EnqueueRunnable
类的源代码之后,这似乎是一个非常刻意的选择。我无法真正理解的是为什么呢?奇怪的是,如果uniqueWorkName
失败了,那么该名称在应用程序的整个生命周期中都将变得无法使用(这种情况会在终止该应用程序的过程中持续存在)。
此外,我想知道是否有人对此有一个好的解决方案,或者想知道这在WorkManager
的未来版本中是否会改变。到目前为止,我能想到的最好的办法是返回Result.SUCCESS
,但在输出Data
中编码我自己的失败状态,以便工作的任何观察者都知道它已经失败了。但是,这有点尴尬,对于以后的代码维护者来说不是很明显(并且在查看给定Work
的日志时可能会有些混乱)。
也许我原本打算使用独特作品的想法是完全错误的,并且有更好的解决方案。任何想法将不胜感激,谢谢!
答案 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
仍然不起作用。通常,考虑:WorkChainA
与CompressWorkerA
和UploadWorkerA
,然后将WorkChainB
与CompressWorkerB
和UploadWorkerB
排队,它们都在同一{{1}下}。取消或失败UniqueWorkName
中的任何Worker
会导致WorkChainA
永远无法运行...因此,我们仍然必须确保每次都返回WorkChainB
。并且不要在Result.success()
中使用任何cancelWorkByTag
或cancelWorkById
!
答案 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