You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WorkManager周期性CoroutineWorker触发JobCancellationException问题求助

WorkManager周期性任务执行10分钟后被取消问题排查

问题描述

创建了一个简单的CoroutineWorker,循环执行1000次,每次延迟1000毫秒。该Worker是唯一周期性任务,重复间隔为15分钟,ExistingPeriodicWorkPolicy设置为KEEP。但启动Worker后,执行约10分钟时任务被取消,抛出JobCancellationException异常。

完整异常信息

Exception kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@ba57765
12:55:47.088 WM-Wor...rapper  I  Work [ id=4c44c3da-3c57-4cac-a40a-82c948125807, tags={ com.sk.workmanagerdemo1.DownloadingWorker } ] was cancelled
                                 java.util.concurrent.CancellationException: Task was cancelled.
                                    at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184)
                                    at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514)
                                    at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
                                    at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:311)
                                    at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
                                    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
                                    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
                                    at java.lang.Thread.run(Thread.java:923)

Worker代码

import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat
import java.util.*

class DownloadingWorker(context: Context, params: WorkerParameters) :
    CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        return withContext(Dispatchers.IO) {
            Log.i("MYTAG", "Started ${getCurrentDateTime()}")
            return@withContext try {
                for (i in 0..1000) {
                    delay(1000)
                    Log.i("MYTAG", "Downloading $i")
                }
                Log.i("MYTAG", "Completed ${getCurrentDateTime()}")
                Result.success()
            } catch (e: Exception) {
                Log.i("MYTAG", "Exception $e")
                Result.failure()
            }
        }
    }

    private fun getCurrentDateTime(): String {
        val time = SimpleDateFormat("dd/M/yyyy hh:mm:ss")
        return time.format(Date())
    }
}

任务启动代码

private fun setPeriodicWorkRequest() {
        val downloadConstraints = Constraints.Builder()
            .setRequiresCharging(true)
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
        val periodicWorkRequest = PeriodicWorkRequest
            .Builder(DownloadingWorker::class.java, 15, TimeUnit.MINUTES)
            .setConstraints(downloadConstraints)
            .build()
        WorkManager.getInstance(applicationContext).enqueueUniquePeriodicWork(
            "DownloadWorker",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }

原因分析

  • WorkManager单次任务超时限制:WorkManager对单次Worker任务的执行时长有默认限制,多数设备约为10分钟。你的Worker单次执行需要1000秒(约16.7分钟),远超限制,系统会自动取消超时任务。
  • 约束条件变化(次要可能):如果执行过程中设备停止充电或网络断开,WorkManager会取消当前任务,但从异常时间点看,超时是主要原因。

解决方案

方案1:拆分长任务为多批次执行

把单次1000次循环拆分成每次执行一部分,通过输入输出参数记录进度,依赖下一次周期性触发继续执行:

override suspend fun doWork(): Result {
    return withContext(Dispatchers.IO) {
        val currentProgress = inputData.getInt("PROGRESS", 0)
        Log.i("MYTAG", "Started from $currentProgress at ${getCurrentDateTime()}")
        return@withContext try {
            val end = min(currentProgress + 100, 1000)
            for (i in currentProgress until end) {
                delay(1000)
                Log.i("MYTAG", "Downloading $i")
            }
            if (end >= 1000) {
                Log.i("MYTAG", "Completed ${getCurrentDateTime()}")
                Result.success()
            } else {
                val outputData = workDataOf("PROGRESS" to end)
                Log.i("MYTAG", "Progress updated to $end, waiting next cycle")
                Result.success(outputData)
            }
        } catch (e: Exception) {
            Log.i("MYTAG", "Exception $e")
            Result.failure()
        }
    }
}

启动任务时初始化进度:

val periodicWorkRequest = PeriodicWorkRequest
    .Builder(DownloadingWorker::class.java, 15, TimeUnit.MINUTES)
    .setConstraints(downloadConstraints)
    .setInputData(workDataOf("PROGRESS" to 0))
    .build()

方案2:使用前台服务避免超时(不推荐用于循环任务)

如果必须单次完成,可将Worker改为前台服务,避免系统后台超时取消,但会显示通知,影响用户体验:

override suspend fun doWork(): Result {
    // 创建前台通知(需提前创建通知渠道,适配Android O+)
    val notification = NotificationCompat.Builder(applicationContext, "DOWNLOAD_CHANNEL")
        .setContentTitle("Downloading")
        .setContentText("In progress...")
        .setSmallIcon(R.drawable.ic_download)
        .build()
    setForeground(ForegroundInfo(1001, notification))
    
    // 原循环执行代码...
}

方案3:调整系统配置(不推荐)

部分厂商设备允许调整后台任务超时,但兼容性差,无法保证所有设备生效,不依赖此方案。

额外建议

  • 避免用WorkManager执行超过10分钟的单次任务,它的设计定位是短时间后台任务或周期性任务。
  • 周期性任务间隔要匹配单次任务时长,避免任务堆积。

内容的提问来源于stack exchange,提问作者sandeep kolhal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:25:27