WorkManager
WorkManager:保留第四版正文机制,以所有者—状态—结果合同、事件轨迹和章专属故障完成可重放验收。
学习目标
- 能沿“WorkManager”的用户事件解释Android组件、状态所有者、线程与销毁边界。
- 能围绕“使用 WorkManager 实现可靠的延迟后台任务:约束条件、任务链、输入输出、唯一任务,以及何时选择 WorkManager 而非 Thread 或 Service。”改出一个可运行结果,并用前后状态而非组件数量验收。
- 能在旋转、进程重建、拒权、离线或无效输入中选择适用反例,定位首个状态分叉。
- 能用WorkRequest状态、约束切换、attempt与唯一工作队列独立重放结论,并标明第四版机制与现代targetSdk政策的边界。
为什么需要"可靠的后台任务"?
你的 App 需要在后台做一件事:把用户编辑的笔记同步到服务器。如果用 Thread { ... } 来做,用户刚点完保存就关了 App、切了网络——线程直接被杀,同步中断,笔记丢失。你怎么办?自己写一套"重试机制"、"任务持久化存储"、"网络状态监听"?
Android 生态历史上出现过一堆解决方案——JobScheduler、AlarmManager、Firebase JobDispatcher、甚至老掉牙的 AsyncTask——它们各有各的坑:有的 API 版本限制、有的不保证执行、有的只对 Google Play Services 用户有效。
↡WorkManager 是 Android Jetpack 中的后台任务调度库,专门解决「保证最终一定执行」这个硬需求。它能处理进程被杀后自动重试、根据网络/充电等条件决定执行时机、以及串联多个任务按顺序执行。就是为了终结这场混乱而生的——它把"可靠地安排后台任务该什么时候做"这件事统一封装。你的任务不丢、不重复、在条件满足时按时执行——即使 App 被杀死、设备重启后也能恢复。
WorkManager 的工作机制
核心三角:Worker + WorkRequest + WorkManager
// 1. Worker:定义"做什么"
class SyncWorker(context: Context, params: WorkerParameters)
: Worker(context, params) {
override fun doWork(): Result {
return try {
syncNotesToServer() // 执行同步逻辑
Result.success() // 成功 → 任务结束
} catch (e: Exception) {
Result.retry() // 失败 → WorkManager 按退避策略自动重试
}
}
}
// 2. WorkRequest:定义"什么时候做"
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 需要网络
.setRequiresCharging(true) // 需要充电
.build()
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(constraints)
.setInitialDelay(10, TimeUnit.MINUTES) // 10 分钟后开始
.build()
// 3. WorkManager:执行调度
WorkManager.getInstance(context).enqueue(syncRequest)三者关系:Worker 定义任务逻辑(doWork),WorkRequest 定义任务的条件和时机(约束 + 延迟 + 重试),WorkManager 做统一调度(入队 + 选择执行时机 + 通知结果)。
Worker(重写 doWork() 返 Result)→ 构建 WorkRequest 并设约束(联网 / 充电 / 空闲 / 电量)→ enqueue 入队 → 约束满足时调度执行(持久化到 DB,进程被杀 / 重启都保证最终执行)→ 可用 beginWith().then() 链式编排。它换的是 可靠性而非即时性,并尊重系统省电(Doze)。:OneTimeWorkRequest(执行一次)和 PeriodicWorkRequest(周期性重复,最小间隔
15 分钟)。
观察任务执行状态
WorkManager 提供 LiveData 接口让你观察任务进展:
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(syncRequest.id)
.observe(lifecycleOwner) { workInfo ->
when (workInfo?.state) {
WorkInfo.State.ENQUEUED -> showStatus("排队等待中...")
WorkInfo.State.RUNNING -> showStatus("正在同步...")
WorkInfo.State.SUCCEEDED -> showStatus("同步成功!")
WorkInfo.State.FAILED -> showStatus("同步失败")
WorkInfo.State.BLOCKED -> showStatus("等待前置任务完成...")
else -> {}
}
}动手看:一条 WorkRequest 的完整生命周期
猜一猜:如果用户在 SyncWorker 的
doWork()执行期间杀掉了 App,同步会中断吗?
① 入队(ENQUEUED)
WorkManager.getInstance(context).enqueue(syncRequest)
把任务注册到内部数据库。这一步后任务已经持久化——即使 App
进程被杀、设备重启,WorkManager 在下次启动时会从数据库中恢复任务。
代码逐段拆解:任务链与数据传递
场景:先压缩图片,再上传到服务器
// 第 1 个 Worker:压缩图片
class CompressWorker(context: Context, params: WorkerParameters)
: Worker(context, params) {
override fun doWork(): Result {
val imageUri = inputData.getString("IMAGE_URI") ?: return Result.failure()
val compressedUri = compressImage(imageUri)
// 输出压缩后的图片路径供下一个 Worker 使用
val output = workDataOf("COMPRESSED_URI" to compressedUri)
return Result.success(output)
}
}
// 第 2 个 Worker:上传
class UploadWorker(context: Context, params: WorkerParameters)
: Worker(context, params) {
override fun doWork(): Result {
val uri = inputData.getString("COMPRESSED_URI") ?: return Result.failure()
return try {
uploadToServer(uri)
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
// 串联执行:先压缩,成功后再上传
val inputData = workDataOf("IMAGE_URI" to photoUri.toString())
val compressRequest = OneTimeWorkRequestBuilder<CompressWorker>()
.setInputData(inputData)
.build()
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
WorkManager.getInstance(context)
.beginWith(compressRequest) // 第一步:压缩
.then(uploadRequest) // 第二步:等压缩成功后上传
.enqueue()唯一任务:防止重复入队
WorkManager.getInstance(context)
.enqueueUniqueWork(
"daily-sync", // 唯一任务名
ExistingWorkPolicy.REPLACE, // 如果已存在同名任务,替换它
syncRequest
)ExistingWorkPolicy 有三种策略:
REPLACE:取消已有任务,用新的替代KEEP:如果已有任务在排队或执行,新的被丢弃APPEND:把新任务加在已有任务后面(形成隐式链)
容易踩的坑
小结
- WorkManager 解决"可靠调度":任务持久化在数据库中,进程被杀或设备重启后自动恢复
- 核心三角:
Worker(定义做什么)→WorkRequest(定义时机和条件)→WorkManager(统一调度) OneTimeWorkRequest一次执行,PeriodicWorkRequest周期执行(最小间隔 15 分钟)beginWith().then()构建任务链,setInputData()+workDataOf()传递数据enqueueUniqueWork()防止重复任务,LiveData+getWorkInfoByIdLiveData()观察状态
练习
问题 1:“第27章 WorkManager”覆盖哪些正式节点和项目主线?
问题 2:怎样建立本页最小可执行实验?
问题 3:为什么只在正常点击路径运行不能证明完成?
问题 4:怎样设计能推翻当前实现的反例?
问题 5:从第4版语境迁移到现代目标SDK时如何控制变量?
问题 6:本页达到独立交接标准需要什么?
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- WorkManager
Android Jetpack 中专门做"可靠后台任务调度"的库。任务存储在内部数据库中,进程被杀或重启后自动恢复。支持约束条件(网络、充电、空闲状态)、任务链、唯一任务、状态观察。是 JobScheduler / AlarmManager / AsyncTask 等老方案的系统性替代。详见本章"WorkManager 的工作机制"一节。
- Worker
定义后台任务逻辑的抽象类。你需要继承 Worker 并实现
doWork()方法,返回Result.success()、Result.failure()或Result.retry()。doWork()默认在后台线程执行,最长 10 分钟。详见本章"核心三角"一节。- OneTimeWorkRequest
只执行一次的后台任务请求。可以设置延迟(
setInitialDelay)、约束条件(setConstraints)、输入数据(setInputData)。队列入队(enqueue)后由 WorkManager 统一调度执行时机。详见本章"核心三角"一节。- PeriodicWorkRequest
按固定时间间隔周期性重复执行的后台任务请求。最小间隔 15 分钟——这是 WorkManager 为防止过度耗电设置的系统下限。不保证精确执行时间,只在间隔窗口内选择一个合适时机执行。详见本章"容易踩的坑"一节。
“WorkManager”不使用未获授权的纸书正文;InformIT出版信息与授权电子版完整目录只用于确认第四版32章、269个正式目录节点和时代语境,第四版官方勘误用于识别工具链变更。下列中文解释、图示、交互、代码与练习均为独立教学重写,平台行为再以Android Developers的一手文档复核。
为什么“WorkManager”必须回到可观察状态
“WorkManager”的学习结果不是记住类名,而是能预测“使用 WorkManager 实现可靠的延迟后台任务:约束条件、任务链、输入输出、唯一任务,以及何时选择 WorkManager 而非 Thread 或 Service。”在一次输入、一次重建和一次失败中的不同状态,并指出哪条Android合同产生差异。
第四版机制逐项深读
27. WorkManager
在“WorkManager”中,分析“27. WorkManager”要记录WorkRequest ID、约束、attempt、输入输出与终态,Worker重试不应重复通知或写入。
Creating a Worker
在“WorkManager”中,“Creating a Worker”描述可延迟且必须最终执行的持久工作;约束决定何时可运行,唯一工作策略决定重复入队如何合并。
Scheduling Work
在“WorkManager”中,分析“Scheduling Work”要记录WorkRequest ID、约束、attempt、输入输出与终态,Worker重试不应重复通知或写入。
Checking for New Photos
在“WorkManager”中,验证“Checking for New Photos”只改变一个生命周期或外部条件,保存操作、原始日志、状态快照和用户可见断言。
Notifying the User
在“WorkManager”中,“Notifying the User”不是精确定时器,也不适合立即UI工作;用户关闭轮询后必须取消唯一工作并验证队列为空。
Providing User Control over Polling
在“WorkManager”中,“Providing User Control over Polling”不是精确定时器,也不适合立即UI工作;用户关闭轮询后必须取消唯一工作并验证队列为空。
“WorkManager”验收回顾
“WorkManager”只有在WorkRequest状态、约束切换、attempt与唯一工作队列能够从相同基线再次得到相同断言时才通过;第四版机制与现代平台政策分别记录,不用新API名称掩盖旧行为。
← 上一页:搜索 · 下一页:broadcast intent →
章专属可重放状态实验
先预测“启用轮询、网络变化、进程重启、重试、通知与关闭轮询”发生后,WorkManager 数据库、PollWorker 与用户设置应怎样改变WorkSpec、约束、attempt、唯一名称、结果和取消状态;再操作三个实验。第四版示例与当前 Android 政策分别记录,实验不把新 API 名称倒填为原书内容。
实验一:所有者—状态—结果合同
选择任一正式目录节点和正常/边界场景,检查它是否真的进入本章状态合同。目录标题只有同时出现在解释、可视状态和交付证据中才算覆盖。
Owner · state · observable result
WorkManager:状态合同
用 Worker、约束、唯一工作、通知与用户开关表达可靠可延期轮询
验证场景
第四版正式目录节点
workmanager · 正常任务
27. WorkManager:固定 SDK、设备配置和初始状态,触发“启用轮询、网络变化、进程重启、重试、通知与关闭轮询”
冻结入口:27. WorkManager
记录WorkManager 数据库、PollWorker 与用户设置的初始WorkSpec、约束、attempt、唯一名称、结果和取消状态
观察:WorkInfo、约束切换、attempt、唯一队列、通知和取消测试中的“27. WorkManager”轨迹
预期:由WorkManager 数据库、PollWorker 与用户设置提交WorkSpec、约束、attempt、唯一名称、结果和取消状态,并持续满足“关闭后唯一工作不存在,重复启用不产生并行轮询”
实验二:事件与生命周期轨迹
沿五次转换逐步执行“启用轮询、网络变化、进程重启、重试、通知与关闭轮询”。每一步只允许WorkManager 数据库、PollWorker 与用户设置按职责提交状态,并持续核对“关闭后唯一工作不存在,重复启用不产生并行轮询”。
Deterministic event replay
WorkManager:事件轨迹
不变量:关闭后唯一工作不存在,重复启用不产生并行轮询
交付证据:WorkInfo、约束切换、attempt、唯一队列、通知和取消测试
实验三:章专属反例与同输入恢复
注入“把 PeriodicWorkRequest 当精确定时器,并重复入队多个同名任务”,保存第一个偏离点;撤销后以完全相同的 SDK、设备状态和用户事件重放。只有WorkInfo、约束切换、attempt、唯一队列、通知和取消测试一起恢复才算修复。
Fault · cancel · restore
WorkManager:反例与恢复
故障:把 PeriodicWorkRequest 当精确定时器,并重复入队多个同名任务
第 1 次使用相同 SDK、设备配置、初始状态与用户事件
保持正常输入不变,仅注入“把 PeriodicWorkRequest 当精确定时器,并重复入队多个同名任务”
关闭后唯一工作不存在,重复启用不产生并行轮询
WorkInfo、约束切换、attempt、唯一队列、通知和取消测试