第一个WorkManager任务:继承Worker、实现doWork
本节通过一个具体的后台同步任务,演示从创建Worker子类、实现doWork方法到在Activity中调度并观察结果的全流程。你将掌握WorkManager最基础的单元——Worker的生命周期与执行约束。
1. 核心原理
WorkManager的任务单元是Worker抽象类。每个后台工作都必须继承它并重写doWork()方法。执行流程如下:
- 调度入队:通过
OneTimeWorkRequest或PeriodicWorkRequest描述任务,并提交给WorkManager.enqueue()。 - 线程分配:WorkManager默认在后台线程池中调用
doWork(),无需手动处理线程切换。 - 结果返回:
doWork()必须返回ListenableWorker.Result(success()/failure()/retry()),WorkManager据此决定任务状态。 - 观察状态:通过
WorkInfoLiveData观察任务的SUCCESS/FAILED/ENQUEUED/RUNNING等状态。
关键设计:Worker的
doWork()在异步线程执行,若返回Result.retry(),WorkManager会根据退避策略自动重试。
2. 实现流程(四步法)
| 步骤 | 操作 | 关键类/方法 |
|---|---|---|
| 1 | 创建Worker子类 | class SyncWorker : Worker() |
| 2 | 实现doWork() | override fun doWork(): Result |
| 3 | 创建 WorkRequest | OneTimeWorkRequestBuilder |
| 4 | 调度并观察 | WorkManager.enqueue() + getWorkInfoByIdLiveData() |
3. 代码实现
3.1 创建Worker子类
新建一个继承Worker的类,并实现doWork()。以下示例模拟一个数据同步任务,执行网络请求后返回成功或失败:
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
class SyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 获取输入数据(可选)
val inputUrl = inputData.getString("url") ?: return Result.failure()
return try {
// 模拟网络请求(此处可替换为实际 Retrofit/OkHttp 调用)
Thread.sleep(2000) // 延迟 2 秒模拟耗时
// 假设同步成功
Result.success()
} catch (e: Exception) {
e.printStackTrace()
// 错误时返回 retry 让 WorkManager 按退避策略重试
Result.retry()
}
}
}
3.2 创建 WorkRequest 并调度
在 Activity 或 ViewModel 中构建 OneTimeWorkRequest 并提交给 WorkManager:
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
// 创建输入数据
val inputData = workDataOf("url" to "https://api.example.com/sync")
// 构建 WorkRequest(可附加约束,此处仅基本示例)
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
.setInputData(inputData)
.build()
// 调度任务
WorkManager.getInstance(context).enqueue(syncRequest)
3.3 观察任务状态
通过 WorkManager 的 getWorkInfoByIdLiveData() 观察任务的生命周期状态:
import androidx.lifecycle.LiveData
import androidx.work.WorkInfo
val workInfoLiveData: LiveData<WorkInfo> =
WorkManager.getInstance(context).getWorkInfoByIdLiveData(syncRequest.id)
workInfoLiveData.observe(this) { workInfo ->
when (workInfo.state) {
WorkInfo.State.ENQUEUED -> { /* 任务已入队 */ }
WorkInfo.State.RUNNING -> { /* 任务正在执行 */ }
WorkInfo.State.SUCCEEDED -> { /* 同步成功 */ }
WorkInfo.State.FAILED -> { /* 失败 */ }
WorkInfo.State.CANCELLED -> { /* 已取消 */ }
else -> { /* 其他状态 */ }
}
}
4. 调试与部署
- 日志输出:在
doWork()内使用Log.d("SyncWorker", "..."),通过adb logcat -s SyncWorker过滤。 - 测试Worker:使用
WorkManagerTestInitHelper在单元测试中直接调用Worker.doWork()。 - 约束条件:正式部署时建议添加
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED)避免无网络时执行。 - 调试利器:在开发者选项中开启“不保留活动”后,WorkManager仍能正常工作,这是验证持久化调度的好方法。
5. 常见错误与解决方案
| 错误现象 | 根因 | 解决办法 |
|---|---|---|
任务无响应,状态卡在ENQUEUED | 缺少INTERNET权限或约束未满足 | 检查AndroidManifest.xml中的权限;移除不必要的约束临时测试 |
doWork()返回Result.failure()但日志无异常 | 业务逻辑异常未捕获 | 在doWork内使用try-catch并打印堆栈 |
| 重复提交导致多个相同任务 | 未使用UniqueWorkPolicy | 使用beginUniqueWork()或enqueueUniqueWork()保证幂等 |
| 任务在电量优化设备上延迟执行 | 系统限制后台执行 | 使用setExpedited()处理高优先级任务(需API 31+或兼容库) |
6. 性能与稳定性优化
- 避免长耗时任务:
doWork()默认有10分钟超时(系统版本差异),超时会被强制标记为failure()。对超过10秒的任务需拆分为多个Worker或使用ListenableWorker协程结合方案。 - 合理使用退避策略:设置
BackoffPolicy.LINEAR或BackoffPolicy.EXPONENTIAL,并指定最小重试间隔(默认30秒)。 - 资源释放:在
doWork()内打开的任何数据库、文件流或网络连接务必在返回前释放。 - 分批执行:大量后台任务(例如列表中每个item都请求)应使用
beginWith+then链式组合,避免线程池耗尽。
实战提示:Worker 在进程被杀死后不会保留对象状态。如需跨重启保存中间结果,请使用
Result.success(outputData)传递输出数据,或结合Room持久化。