WorkManager 基础概念与 API 入门
WorkManager 是 Android Jetpack 中用于可延迟、可保证执行的后台任务调度库。它与应用生命周期深度集成,即使应用被杀死或设备重启,任务也会在合适时机恢复执行。本章围绕 Worker、WorkRequest、WorkManager.getInstance() 以及任务调度与状态观察四个核心 API,梳理其原理、实现流程与常见陷阱。
核心思想:WorkManager 将任务定义(Worker)、任务配置(WorkRequest)与任务调度(WorkManager)分离,并通过 LiveData 或 Flow 提供可观察的状态更新。
1. Worker 基类:任务定义单元
所有后台任务都需要继承 Worker 类并重写 doWork() 方法。该方法运行在 WorkManager 提供的后台线程上,返回 Result 以告知调度器任务是否成功。Worker 是单次执行模型,如果需要周期性任务则使用 PeriodicWorkRequest。
| Result 类型 | 含义 | 重试行为 |
|---|---|---|
Result.success() | 任务正常完成 | 不重试 |
Result.retry() | 任务需重试(受重试策略控制) | 按退避策略重试 |
Result.failure() | 任务失败,不再重试 | 不重试 |
class UploadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
val inputData = inputData.getString("file_path") ?: return Result.failure()
return try {
uploadFile(inputData)
Result.success()
} catch (e: IOException) {
Result.retry()
}
}
}
2. WorkRequest:任务描述与配置
WorkRequest 封装 Worker 与运行时配置(约束、重试策略、输入数据、标签等)。主要分为两种:
| 类型 | 创建方式 | 执行次数 | 最小间隔 |
|---|---|---|---|
| OneTimeWorkRequest | OneTimeWorkRequestBuilder<Worker>() | 1 次 | 无 |
| PeriodicWorkRequest | PeriodicWorkRequestBuilder<Worker>(15, TimeUnit.MINUTES) | 多次(周期性) | ≥ 15 分钟(实际间隔来自后台限制) |
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(workDataOf("file_path" to "/sdcard/tmp.txt"))
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
.addTag("upload")
.build()
使用
setBackoffCriteria() 控制重试间隔:默认线性退避,建议关键任务使用指数退避以减少服务器压力。3. WorkManager 实例与任务提交
WorkManager.getInstance() 返回全局单例,无需手动初始化(应用启动时自动通过 ContentProvider 初始化)。调用 enqueue() 将 WorkRequest 提交到内部队列。
val workManager = WorkManager.getInstance(context)
workManager.enqueue(uploadRequest)
也可使用 enqueueUniqueWork() 或 beginUniqueWork() 进行链式任务编排。
4. 任务状态与实时观察
通过 getWorkInfoByIdLiveData() 获取 LiveData<WorkInfo>,观察状态变化。WorkInfo 包含:
- State:ENQUEUED、RUNNING、SUCCEEDED、FAILED、CANCELLED、BLOCKED
- outputData:Worker 通过
Result.success(outputData)传回结果 - tags:关联的标签集合
workManager.getWorkInfoByIdLiveData(uploadRequest.id)
.observe(viewLifecycleOwner) { workInfo: WorkInfo ->
when (workInfo.state) {
WorkInfo.State.SUCCEEDED -> { /* 处理成功 */ }
WorkInfo.State.FAILED -> { /* 处理失败 */ }
else -> { /* 其他状态 */ }
}
}
状态观察必须在主线程注册(LiveData 自动保证),但 doWork() 中切勿直接更新 UI,应通过状态观察回调或共享 ViewModel 传递结果。
5. 调试方法与常见陷阱
- 启用调试日志:在
AndroidManifest.xml中添加<meta-data android:name="androidx.work.util.Logging" android:value="INFO" />或在Application.onCreate()中调用Logging.setLoggingCallback。 - 约束不满足导致不执行:例如网络约束或存储约束未达到,任务会一直处于 ENQUEUED 状态。使用
WorkManager.getWorkInfosByTagLiveData(tag)观察可排查。 - 重试策略误用:
Result.retry()配合BackoffPolicy.LINEAR可能造成频繁重试,建议使用EXPONENTIAL并设置合理初始延迟(默认 30 秒)。 - Worker 中的静态状态:Worker 实例在每次调度时重新创建,不要在 Worker 中持有 Activity 或 View 引用,否则导致内存泄漏。
6. 性能考量与最佳实践
- doWork() 保持轻量:系统会给 Worker 分配一个后台线程,但长时间阻塞(如 > 10 秒)可能被系统视为 ANR 或延迟其他任务。网络请求、文件操作应使用异步 I/O 或考虑使用
CoroutineWorker(后续章节展开)。 - 合理使用约束:只设置必要的约束(如
NetworkType.CONNECTED),避免过度约束导致任务迟迟不执行。 - 数据合并:当多个 WorkRequest 输出到同一链式任务时,使用
InputMerger(如ArrayCreatingInputMerger)避免数据覆盖。 - 避免重复 enqueue:使用
enqueueUniqueWork(ExistingWorkPolicy.KEEP)防止相同标识的任务重复入队。