WorkManager为何重复执行已成功完成的Worker?
问题描述
我无法理解为何WorkManager会将Worker运行两次(并非每次运行都会出现此情况):
enqueueWorkers方法接收仅含一个元素的数组,并将其作为无退避策略的OneTimeWorkerRequest入队;- 即便数组包含多条数据(每条数据的trigger id均来自数据库且唯一),
doWork()方法仍能无异常成功执行——executor.waitFinish会向数据库写入日志,显示执行过程符合预期且已正常结束。
但在Android Studio的后台检查器中,看到该任务的运行次数为2,重试次数为1,这并非重复请求,而是同一任务被重新执行。
疑问:WorkManager是否在我不知情的情况下捕获了异常?或是导致已成功任务被重跑的原因是什么?

相关代码实现
任务入队方法
private fun enqueueProgramWorkers(triggers: List<QueueDateTriggerModel>) { if (triggers.isEmpty()) { return } val triggerRequests = triggers.map { val tag = createTriggerTag(it.trigger.id) workManager.cancelAllWorkByTag(tag) OneTimeWorkRequestBuilder<ProgramWorker>() .setInputData(ProgramWorker.workDataOf(it.trigger.programId, EnqueueSource.DATE_TRIGGER, it.trigger.id)) .setInitialDelay(it.delayToLaunch, TimeUnit.MILLISECONDS) .addTag(tag) .build() } workManager.enqueue(triggerRequests) }
Worker实现类
@HiltWorker class ProgramWorker @AssistedInject constructor( @Assisted private val context: Context, @Assisted params: WorkerParameters, private val executor: Executor, ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result = withContext(ioScope.coroutineContext) { val model = ProgramWorkerModel(inputData) setForeground() executor.waitFinish(model.programId) return@withContext Result.success() } suspend fun setForeground() { try { setForeground(getForegroundInfo()) } catch (error: IllegalStateException) { Log.d("ProgramWorker ", error.message.toString()) } } companion object { fun workDataOf(programId: Int, enqueueSource: EnqueueSource, triggerId: Int = NEW_ID): Data { return workDataOf( PROGRAM_ID to programId, ENQUEUE_SOURCE to enqueueSource.name, TRIGGER_ID to triggerId ) } } }
依赖版本
implementation "androidx.work:work-runtime-ktx:2.10.0"
可能的原因及排查方向
Foreground Service 状态异常
你的Worker中调用了setForeground()但捕获了IllegalStateException,如果这个方法调用失败(比如Android 12+上未正确配置通知渠道、通知权限被拒绝),WorkManager可能会判定Worker执行异常并触发重试。虽然你捕获了异常,但WorkManager内部仍可能感知到状态异常,建议检查getForegroundInfo()的实现是否合规,确保通知渠道已创建、通知内容符合系统要求。Worker进程被系统杀死
即便doWork()返回Result.success(),如果Worker所在进程在结果被WorkManager持久化前被系统杀死,WorkManager会认为任务未完成,重新执行该任务。可以在doWork()末尾添加明确的完成日志,同时监控系统进程存活状态,确认是否存在进程被回收的情况。WorkManager内部状态同步问题
2.10.0版本的WorkManager虽相对稳定,但仍可能存在极少数内部状态不一致的情况。可以尝试升级到最新稳定版本,同时查看WorkManager默认数据库workdb中WorkSpec表的任务记录,确认第一次执行后任务的state字段是否被正确标记为SUCCEEDED。任务取消与入队的时序冲突
在enqueueProgramWorkers方法中,你先调用cancelAllWorkByTag(tag)再创建并入队新任务。如果取消操作未完成就执行入队,可能导致WorkManager内部任务状态冲突,触发重复执行。可以改为先构建请求,再执行取消+入队的原子操作,或者使用enqueueUniqueWork替代普通入队来避免重复任务。
内容的提问来源于stack exchange,提问作者Viewed

