第一章:WorkManager 简介与核心概念

1.1 什么是 WorkManager

WorkManager 是 Android Jetpack 提供的后台任务调度库,用于执行可延迟、需保证完成的后台工作。它根据系统版本、设备状态和应用生命周期自动选择底层实现(JobScheduler / AlarmManager + BroadcastReceiver),开发者无需关心兼容性问题。

特性说明
最低 APIAPI 14+(带 Google Play 服务),API 23+(原生)
任务保证即使应用被关闭或设备重启,任务仍会执行(使用 Room 持久化状态)
生命周期感知自动感知 Fragment/Activity/Service 生命周期
约束条件支持网络、电量、存储、空闲等运行时约束
协程集成通过 CoroutineWorker 天然支持 Kotlin 协程

1.2 调度原理:底层分发策略

WorkManager 的核心调度逻辑由 WorkManagerImpl 驱动,决策流程如下:

  1. 开发者创建 OneTimeWorkRequest 或 PeriodicWorkRequest,并通过 WorkManager.enqueue() 提交。
  2. 请求进入 WorkManager 内部数据库(Room),状态设为 ENQUEUED。
  3. 调度器 Scheduler 根据当前设备 API 选择合适实现:
    • API 23+:使用 JobSchedulerScheduler,将任务委托给 JobScheduler。
    • API 14~22:使用 AlarmManagerScheduler + BroadcastReceiver 组合。
  4. 同时支持 GreedyScheduler(贪婪调度器),在应用进程存活时立即尝试执行任务,延迟低于系统级调度。
  5. 任务执行完成后,状态更新为 SUCCEEDED、FAILED 或 CANCELLED。

关键点:WorkManager 并不保证实时性,它保证的是“最终一定会执行”(Best Effort + 保证完成)。协程结合时,CoroutineWorker 会在后台协程作用域内挂起,直到任务完成或取消。

WorkManager 调度核心流程 开发者 enqueue() Room 持久化 调度决策 JobScheduler AlarmManager GreedyScheduler 执行 Worker 状态更新到 DB 📌 状态流转:ENQUEUED → RUNNING → (SUCCEEDED | FAILED | CANCELLED)

1.3 工作约束(Constraints)

WorkManager 允许在任务上绑定 约束条件,只有满足约束时任务才会执行。常用约束列表:

约束类型API 方法说明
网络类型setRequiredNetworkType(NetworkType)UNMETERED / CONNECTED / NOT_ROAMING / METERED 等
电量充足setRequiresCharging(true)设备正在充电时才执行
设备空闲setRequiresDeviceIdle(true)设备处于空闲(Doze mode)状态
存储空间setRequiresStorageNotLow(true)存储空间不低
电池电量setRequiresBatteryNotLow(true)电池电量不低(API 26+)
内容触发器setContentUriTriggers(...)特定 ContentUri 发生变化时触发(API 24+)

最佳实践:约束条件越多,任务延迟执行的概率越大。在协程结合场景中,建议仅在必要条件下添加约束,避免任务永久挂起。同时配合 WorkRequest.addTag() 便于批量观察和管理。

1.4 工作状态(Work States)

每个 WorkRequest 在生命周期内经历以下状态(存储在 Room 数据库中):

状态枚举值含义
ENQUEUEDState.ENQUEUED任务已入队,等待满足约束并开始执行
RUNNINGState.RUNNING任务正在执行(Worker 的 doWork 方法正在运行)
SUCCEEDEDState.SUCCEEDED任务成功完成(返回 Result.success())
FAILEDState.FAILED任务失败(返回 Result.failure());仅一次性任务最终状态
CANCELLEDState.CANCELLED任务被取消(调用 cancelWorkById 或 cancelAllWorkByTag)
BLOCKEDState.BLOCKED任务被前置依赖任务阻塞(已弃用,API 30+ 统一用 ENQUEUED 表示)

1.5 与协程结合的基础

使用 CoroutineWorker 代替 Worker,可以在 doWork() 中直接调用挂起函数:

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

    override suspend fun doWork(): Result {
        // 在这里可以调用挂起函数(如网络请求、数据库操作)
        val imageData = getImageDataFromDatabase()
        val response = apiService.upload(imageData)
        return if (response.isSuccessful) {
            Result.success()
        } else {
            Result.retry()
        }
    }
}

注意:CoroutineWorker 的 doWork() 运行在默认的 Dispatchers.Default 协程上下文。如果需要主线程操作,应使用 withContext(Dispatchers.Main) 切换。不要直接使用 GlobalScope 或手动创建协程,否则可能导致生命周期泄漏或任务提前结束。

1.6 常见错误与调试

  • 任务未执行:检查是否缺少必要的权限(如 FOREGROUND_SERVICE、RECEIVE_BOOT_COMPLETED)。
  • 状态一直 ENQUEUED:可能是约束条件未满足(如要求充电但设备未插电),可通过 WorkManager.getWorkInfoById() 查看约束详情。
  • Result.retry() 导致无限循环:搭配 setBackoffCriteria() 设置退避策略(线性/指数),并限制重试次数。
  • 协程内异常未被捕获:CoroutineWorker 中的未捕获异常会导致任务状态变为 FAILED,建议使用 try-catch 包裹挂起调用。
// 调试示例:获取任务信息
val workInfo = WorkManager.getInstance(context).getWorkInfoById(workRequestId).await()
Log.d("WorkManager", "状态: ${workInfo.state}, 约束: ${workInfo.constraints}")

1.7 性能与稳定性要点

方面建议
任务粒度单个任务执行时间不宜超过 10 分钟(系统会触发 ANR 或强制结束)
内存占用避免在 CoroutineWorker 内持有大型 Bitmap 或集合,尽量使用文件流
生命周期使用 WorkManager.enqueueUniqueWork() 防止重复任务堆积
协程取消任务被取消时协程作用域自动取消,应在挂起点检查 isActive 或 ensureActive() 及时响应
数据持久化使用 Data.Builder 传递小数据(< 10KB);大文件通过 Uri 或文件路径传入

本章小结:WorkManager 的核心是“基于持久化状态的可延迟保证调度”,通过约束控制执行时机,通过状态机管理生命周期。协程结合后,后台任务可以像普通同步代码一样编写,同时享受挂起与调度的优势。下一章将深入构建第一个 CoroutineWorker 示例。