第一个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据此决定任务状态。
  • 观察状态:通过WorkInfo LiveData观察任务的SUCCESS/FAILED/ENQUEUED/RUNNING等状态。
关键设计:Worker的doWork()在异步线程执行,若返回Result.retry(),WorkManager会根据退避策略自动重试。
Worker 生命周期与执行流程 创建 WorkRequest enqueue() doWork() 执行 Result.success() Result.failure() Result.retry() LiveData观察 说明:Worker 在执行 doWork() 前会通过 Context 注入 Application 上下文。 结果类型决定任务最终状态,与 WorkManager 的重试策略联动。

2. 实现流程(四步法)

步骤操作关键类/方法
1创建Worker子类class SyncWorker : Worker()
2实现doWork()override fun doWork(): Result
3创建 WorkRequestOneTimeWorkRequestBuilder
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持久化。