4. WorkManager初始化与基础任务:WorkRequest定义、Worker实现、输入输出数据、后台线程约束
4.1 核心原理
WorkManager 是 Android 官方推荐的持久化任务调度组件,适用于可延迟、需保证执行的后台操作。其核心模型围绕三个抽象层构建:
- Worker ── 任务执行单元。继承
Worker并重写doWork()方法,该方法在 WorkManager 提供的后台线程中运行。 - WorkRequest ── 任务配置载体。封装 Worker 类引用、输入数据、约束条件、标签、重试策略等元信息。
- WorkManager ── 调度引擎。负责任务入队、约束匹配、线程分配、生命周期管理和持久化恢复。
基础任务的生命周期遵循 定义 → 配置 → 提交 → 调度 → 执行 → 回传结果 的线性路径。输入数据通过 Data 对象传入 Worker;执行完毕后通过 Result 返回输出数据或状态。
4.2 Worker 实现
Worker 是任务逻辑的载体。继承 Worker(context, params) 并重写 doWork(),该方法在 WorkManager 的后台线程中同步调用,返回值决定任务终态。
Worker 接口契约:
doWork() 必须同步返回 Result,不允许在该方法内启动异步线程并立即返回。所有耗时操作应直接在方法体内顺序执行。4.2.1 基本 Worker 模板
class CacheCleanupWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
override fun doWork(): Result {
// 1. 获取输入数据
val maxSizeMb = inputData.getLong("max_size_mb", 50L)
// 2. 执行清理逻辑(同步)
val cleanedSize = performCleanup(maxSizeMb)
// 3. 构造输出数据
val output = workDataOf("cleaned_size" to cleanedSize)
return Result.success(output)
}
private fun performCleanup(maxSizeMb: Long): Long {
val cacheDir = applicationContext.cacheDir
// 模拟清理操作
return 0L
}
}
4.2.2 返回值语义
| Result | 含义 | 重试行为 |
|---|---|---|
Result.success() | 任务执行成功,可附带输出 Data | 不重试 |
Result.failure() | 任务执行失败,可附带输出 Data | 不重试(除非配置重试策略) |
Result.retry() | 任务需要重试,触发退避策略 | 按 BackoffPolicy 重试 |
⚠️ 线程规则:WorkManager 自动将
doWork() 调度到后台线程,禁止在该方法内直接操作 UI 或启动未管理的协程/线程。若需异步,应使用 ListenableWorker / CoroutineWorker。4.3 WorkRequest 定义
WorkRequest 是 Worker 的配置外壳,通过 Builder 模式组装。最常用的两种类型为 OneTimeWorkRequest(单次任务)和 PeriodicWorkRequest(周期任务)。
4.3.1 OneTimeWorkRequest
val cleanRequest = OneTimeWorkRequestBuilder<CacheCleanupWorker>()
.setInputData(workDataOf("max_size_mb" to 100))
.setConstraints(constraints)
.addTag("cleanup")
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
30, TimeUnit.SECONDS
)
.build()
4.3.2 PeriodicWorkRequest
val periodicRequest = PeriodicWorkRequestBuilder<CacheCleanupWorker>(
1, TimeUnit.HOURS
)
.setConstraints(constraints)
.addTag("periodic_cleanup")
.build()
| 特性 | OneTimeWorkRequest | PeriodicWorkRequest |
|---|---|---|
| 执行次数 | 单次 | 周期性重复 |
| 最小间隔 | 无限制 | 15 分钟 |
| 适用场景 | 一次性上传、清理、同步 | 定期缓存刷新、日志上报 |
| 约束支持 | 全部支持 | 全部支持 |
| 重试策略 | 支持自定义退避 | 不支持(下一次周期重新执行) |
4.4 输入输出数据
WorkManager 使用 Data 对象在外部和 Worker 之间传递数据。Data 本质是一个键值对容器,内部序列化为字节数组持久化存储。
4.4.1 输入数据构造
// 使用 workDataOf 工厂方法
val inputData = workDataOf(
"url" to "https://example.com/data",
"timeout" to 30,
"compress" to true
)
// 使用 Data.Builder
val inputData2 = Data.Builder()
.putString("url", "https://example.com/data")
.putInt("timeout", 30)
.putBoolean("compress", true)
.build()
4.4.2 Worker 内读取输入
val url: String? = inputData.getString("url")
val timeout: Int = inputData.getInt("timeout", 15)
val compress: Boolean = inputData.getBoolean("compress", false)
4.4.3 输出数据返回
val outputData = workDataOf(
"bytes_downloaded" to 2048L,
"success" to true
)
return Result.success(outputData)
4.4.4 外部获取输出(任务链/观察)
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(request.id)
.observe(this) { info ->
if (info.state == WorkInfo.State.SUCCEEDED) {
val bytes = info.outputData.getLong("bytes_downloaded", 0L)
}
}
💡 数据大小限制:单个
Data 对象序列化后不得超过 10 KB(10240 字节)。超出限制会抛出 IllegalStateException。大数据应使用文件、Room 或 ContentProvider 传递。4.5 后台线程约束
Constraints 让任务仅在满足特定设备条件时执行,避免在电量低或网络不可用时触发耗时操作。所有约束通过 Constraints.Builder 组合设置。
4.5.1 约束类型一览
| 约束方法 | 数据类型 | 说明 | 典型场景 |
|---|---|---|---|
setRequiredNetworkType() | NetworkType | 网络连接条件 | 上传下载任务 |
setRequiresBatteryNotLow() | Boolean | 电量充足 | 耗电的本地计算 |
setRequiresStorageNotLow() | Boolean | 存储空间充足 | 写入大量缓存 |
setRequiresDeviceIdle() | Boolean | 设备空闲(API 23+) | 批量索引、备份 |
setRequiresCharging() | Boolean | 正在充电 | 数据迁移、更新 |
4.5.2 NetworkType 枚举
| 枚举值 | 含义 |
|---|---|
NOT_REQUIRED | 不限制网络(默认) |
CONNECTED | 有可用网络连接 |
UNMETERED | 非计费网络(Wi-Fi / 以太网) |
METERED | 计费网络(移动数据) |
TEMPORARILY_UNMETERED | 临时非计费(热点等) |
4.5.3 组合约束示例
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.setRequiresStorageNotLow(true)
.build()
⚠️ 约束误用:过度严格的约束组合可能导致任务长时间无法触发。建议为关键任务保留 至少一个宽松条件,或使用
NetworkType.CONNECTED 而非 UNMETERED 作为兜底。4.6 完整实现流程
- 定义 Worker:继承
Worker,在doWork()中编写同步任务逻辑并返回Result。 - 构造输入 Data:使用
workDataOf()或Data.Builder封装键值对,大小 ≤ 10 KB。 - 设置 Constraints:通过
Constraints.Builder按需添加网络、充电、空闲等条件。 - 构建 WorkRequest:使用
OneTimeWorkRequestBuilder或PeriodicWorkRequestBuilder装配 Worker 类、输入数据、约束、标签和重试策略。 - 提交任务:调用
WorkManager.getInstance(context).enqueue(request)将任务加入调度队列。 - 观察执行结果:通过
WorkInfoLiveData 或回调获取任务终态和输出数据。
// 完整组合示例
class SyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
val url = inputData.getString("sync_url") ?: return Result.failure()
// 执行同步...
return Result.success(workDataOf("synced" to true))
}
}
val syncConstraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(false)
.build()
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
.setInputData(workDataOf("sync_url" to "https://api.example.com/sync"))
.setConstraints(syncConstraints)
.addTag("sync")
.build()
WorkManager.getInstance(context).enqueue(syncRequest)
4.7 调试与验证
任务提交后可通过以下方式观察状态与数据流:
- adb 命令:
adb shell dumpsys jobscheduler查看系统 JobScheduler 队列中 WorkManager 的任务状态。 - WorkManager 诊断:
adb shell am broadcast -a "androidx.work.diagnostics.REQUEST_DIAGNOSTICS"触发 WorkManager 输出诊断日志。 - LiveData 观察:使用
getWorkInfoByIdLiveData()或getWorkInfosByTagLiveData()实时监听任务状态变化。 - 日志输出:在
doWork()中添加Log.d输出执行时间、输入参数和结果,方便定位问题。
调试技巧:在开发阶段可调用
WorkManager.getInstance(context).cancelAllWork() 清除所有排队任务,避免旧任务干扰新逻辑测试。4.8 常见错误与优化
| 常见错误 | 原因 | 解决方案 |
|---|---|---|
IllegalStateException: Data size too large | 序列化后的 Data 超过 10 KB | 改用文件、Room 或分片传递 |
doWork() 返回后任务未执行 | 约束条件过于严格或设备状态不满足 | 放宽约束组合,添加日志观察状态 |
| 任务重复执行多次 | 未设置唯一工作名称或标签冲突 | 使用 beginUniqueWork() 或 enqueueUniqueWork() |
| UI 线程被阻塞 | 在 doWork() 中执行了网络请求或数据库写操作 | 确保所有操作在 doWork() 同步体内完成,WorkManager 已运行在后台线程 |
| 输入数据读取为 null | 键名拼写错误或未设置默认值 | 使用带默认值的 getter,如 getInt("key", 0) |
💡 性能优化建议:
① 任务粒度适中,避免单个 Worker 处理过多数据,拆分为多个子任务并行执行;
② 善用
③ 合理设置退避策略,避免频繁重试耗尽电池;
④ 周期性任务间隔不要低于 15 分钟,否则 WorkManager 会自动调整。
① 任务粒度适中,避免单个 Worker 处理过多数据,拆分为多个子任务并行执行;
② 善用
addTag() 对任务分组,便于批量取消或观察;③ 合理设置退避策略,避免频繁重试耗尽电池;
④ 周期性任务间隔不要低于 15 分钟,否则 WorkManager 会自动调整。