第3章 Worker基本结构:CoroutineWorker与RxWorker选择、doWork方法定义、返回Result

3.1 Worker基本结构概述

WorkManager 中的 Worker 是任务执行的最小单元。Android 提供了三种 Worker 基类:Worker(同步)、CoroutineWorker(协程)和 RxWorker(RxJava)。在实际测试与生产环境中,由于网络请求(Retrofit)天然适合异步模型,我们主要关注后两种。本章聚焦 doWork() 方法的核心定义、返回结果类型以及如何根据项目异步栈选择 Worker 类型。

3.2 CoroutineWorker vs RxWorker 选择

两种 Worker 都将异步操作暴露给 WorkManager,但底层机制不同,直接影响测试写法与错误处理。

对比维度 CoroutineWorker RxWorker
异步模型 Kotlin 协程(suspend) RxJava Observable/Single/Completable
doWork定义 suspend fun doWork(): Result fun createWork(): Single<Result>
适用场景 新项目、统一协程栈、Retrofit 挂起函数 遗留 RxJava 项目、已有 Response 流需要转换
错误处理 try/catch 或 CoroutineExceptionHandler RxJava onError 回调
重试机制 返回 Result.retry() 后自动重试 返回 Single.error(new RetryException()) 配合自定义策略
测试依赖 kotlinx-coroutines-test, TestCoroutineDispatcher RxJava TestObserver, TestScheduler
选择建议:如果项目中 Retrofit 接口已使用 suspend fun,优先选择 CoroutineWorker,测试代码更简洁;若已大量使用 RxJava 进行响应式编排,则可继续使用 RxWorker 以保持一致性。

3.3 doWork 方法定义

两种 Worker 的 doWork 定义在语法上有明显差异,但语义相同:返回一个 Result 对象通知 WorkManager 任务状态。

3.3.1 CoroutineWorker

class SyncWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        // 获取输入数据
        val inputData = inputData.getString("KEY_URL") ?: return Result.failure()

        return try {
            // 假设 Retrofit 挂起函数
            val response = apiService.fetchData(inputData)
            // 处理成功逻辑
            Result.success()
        } catch (e: Exception) {
            // 可重试的异常(如网络超时)
            if (runAttemptCount < 3) {
                Result.retry()
            } else {
                Result.failure()
            }
        }
    }
}

3.3.2 RxWorker

class RxSyncWorker(context: Context, params: WorkerParameters) : RxWorker(context, params) {

    override fun createWork(): Single<Result> {
        val inputData = inputData.getString("KEY_URL") ?: return Single.just(Result.failure())

        // 假设 apiService 返回 Single<Response>
        return apiService.fetchDataRx(inputData)
            .map { response ->
                // 成功输出
                Result.success()
            }
            .onErrorReturn { error ->
                // 根据错误类型返回 retry 或 failure
                if (runAttemptCount < 3) {
                    Result.retry()
                } else {
                    Result.failure()
                }
            }
    }
}

3.4 返回 Result 详解

Result 是 sealed 类,包含三种状态:

  • Result.success():任务正常完成,WorkManager 不再重试。
  • Result.failure():任务永久失败,WorkManager 不再重试,且会触发失败监听。
  • Result.retry():任务需要重试。WorkManager 会根据 BackoffPolicy 和 runAttemptCount 自动调度重试。

三种 Result 都不支持携带数据。若需要传递结果给下一个 Worker,必须使用 Result.success(outputData) 和 Data.Builder。

// 带输出数据的 Result(仅 CoroutineWorker 可用)
val output = workDataOf("RESULT_KEY" to "some value")
Result.success(output)

// RxWorker 通过 Single<Result> 返回,类似
Single.just(Result.success(workDataOf("KEY" to "val")))
常见错误:不要混淆 Result(WorkManager 返回类型)与 Kotlin 标准库的 kotlin.Result。WorkManager 的 Result 全限定名为 androidx.work.ListenableWorker.Result。代码中务必导入正确的包。

3.5 整体流程与生命周期

下图展示了一个 Worker 从创建到返回 Result 的完整生命周期,以及 Result 如何影响后续行为。

Worker 生命周期与 Result 决策流程 Worker 实例创建 doWork() 执行 Result.success() Result.retry() Result.failure() 任务完成 / 失败通知 BackoffPolicy 计算 runAttemptCount++ 延迟后重返 doWork

3.6 常见错误与调试

  • 返回类型错误:CoroutineWorker 中若忘记 suspend 关键字,编译报错;RxWorker 若返回 Observable 而非 Single,WorkManager 无法正确订阅。
  • 线程冲突:在 RxWorker 的 createWork() 中做耗时操作未切换线程,会阻塞 Worker 调度线程,导致 ANR。
  • 重试条件判断失效:没有检查 runAttemptCount 就不断返回 Result.retry(),造成无限重试。
  • 测试中异步超时:使用 CoroutineWorker 测试时,未设置 TestCoroutineDispatcher 的 advanceUntilIdle,导致测试阻塞。
调试技巧:在 doWork() 中通过 Log.d("Worker", "attempt=$runAttemptCount") 打印重试次数;使用 WorkManager 的 getWorkInfoByIdLiveData() 监听 Worker 状态变化。

3.7 小结