4. 创建同步Worker类:继承Worker、重写doWork、返回Result成功/失败/重试

在本章中,你将学习如何创建自定义的 Worker 类,这是 WorkManager 执行后台同步任务的核心组件。我们将深入探讨 doWork() 方法的实现、三种 Result 返回值的含义及其使用场景,以及如何利用重试机制实现可靠的网络同步。

4.1 Worker 类的工作原理

WorkManager 通过 Worker 类来定义后台任务。系统会在单独的线程上调用 doWork() 方法,并在该方法返回后决定任务的最终状态。Worker 的生命周期由 WorkManager 管理,开发者只需关注同步逻辑的实现。

Worker 同步任务生命周期与 Result 流转 Worker doWork() 生命周期与 Result 流转 WorkManager 调度 doWork() 执行 Result.success() 任务完成,不再执行 Result.failure() 任务失败,不重试 Result.retry() 任务失败,按策略重试

4.2 基础 Worker 实现

创建一个继承自 Worker 的类,必须重写 doWork() 方法。该方法在后台线程执行,不应执行 UI 操作。

import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import retrofit2.Response

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

    override fun doWork(): Result {
        return try {
            // 获取输入数据(可选,用于传递参数)
            val serverUrl = inputData.getString("server_url") ?: "https://api.example.com"
            
            // 执行网络同步逻辑
            val apiService = RetrofitClient.getInstance(serverUrl)
            val response = apiService.uploadData(payload).execute()
            
            if (response.isSuccessful) {
                // 同步成功
                Result.success()
            } else {
                // 服务器返回错误,但非网络问题,不重试
                Result.failure()
            }
        } catch (e: IOException) {
            // 网络异常,适合重试
            Result.retry()
        } catch (e: Exception) {
            // 其他异常,根据业务决定是否重试
            Result.failure()
        }
    }
}

4.3 Result 返回值的三种类型

Result 类型 含义 后续行为 适用场景
Result.success() 任务成功完成 WorkManager 标记任务完成,不再执行 数据上传成功、同步完成、操作正常结束
Result.failure() 任务失败,无需重试 WorkManager 标记任务失败,不再执行 服务器返回业务错误、参数无效、权限不足
Result.retry() 任务失败,需要重试 WorkManager 根据重试策略安排下次执行 网络超时、DNS 解析失败、服务器 503
最佳实践: 在网络异常(如 IOException、SocketTimeoutException)时始终返回 Result.retry();对于业务逻辑错误(如 404、422 等 HTTP 状态码),返回 Result.failure(),避免无意义的重复请求。

4.4 配置重试策略(Backoff Criteria)

当返回 Result.retry() 后,WorkManager 会按照预定义的重试策略重新调度任务。可以通过 OneTimeWorkRequest.Builder 配置重试行为:

import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit

val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
    .setInputData(inputData)
    // 设置重试后退策略:线性,初始延迟 30 秒
    .setBackoffCriteria(
        BackoffPolicy.LINEAR,
        OneTimeWorkRequestBuilder.MIN_BACKOFF_MILLIS,
        TimeUnit.MILLISECONDS
    )
    .build()

WorkManager.getInstance(context)
    .enqueueUniqueWork(
        "sync_work",
        ExistingWorkPolicy.KEEP,  // 如果已有同名任务,保留原有
        syncRequest
    )
重试策略参数说明:
  • BackoffPolicy.LINEAR:每次重试延迟时间线性增加(如 30s、60s、90s……)
  • BackoffPolicy.EXPONENTIAL:每次重试延迟时间指数级增加(如 30s、60s、120s……)
  • 最小后退延迟默认为 10 秒,最大不超过 10 分钟(由 WorkManager 内部限制)

4.5 doWork() 中的线程与超时处理

WorkManager 为 doWork() 提供了默认的后台线程,不需要手动切换线程。但网络请求本身需要合理设置超时时间,避免 Worker 被长时间阻塞。

// 在 RetrofitClient 中配置超时
val okHttpClient = OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .build()

// WorkManager 默认 worker 执行超时为 10 分钟
// 可通过 setInitialRunAttemptCount() 设置初始重试次数

4.6 调试与测试 Worker

  • 使用 WorkManager 测试辅助类: TestWorker、WorkManagerTestInitHelper 可以在单元测试中模拟 Worker 执行。
  • 日志输出: 在 doWork() 中打印关键日志,结合 adb logcat 查看 Worker 运行状态。
  • 手动触发: 在开发阶段可以通过 WorkManager.getInstance().enqueue() 直接触发 Worker,无需等待系统调度。
adb logcat -s "SyncWorker"  // 过滤 SyncWorker 标签的日志
常见错误:
  • 忘记在 doWork() 中使用 .execute()(同步调用)而不是 .enqueue()(异步回调)。WorkManager 需要阻塞等待网络结果。
  • 在 doWork() 中直接访问 UI 或 Application 上下文之外的视图,导致内存泄漏。
  • 返回 Result.retry() 后未配置 setBackoffCriteria(),导致使用默认重试策略(10 秒后重试,最多 3 次)。

4.7 性能与稳定性优化建议

  • 避免重复创建 Retrofit 实例: 使用单例模式或依赖注入,复用 OkHttpClient 和 Retrofit 对象。
  • 控制重试次数: 通过 setInitialRunAttemptCount() 限制最大重试次数,防止无限制重试耗尽电量与流量。
  • 数据传递优化: 使用 Data.Builder 传递轻量级参数,避免传递大对象(如图片 Base64),建议在 Worker 内部读取本地文件。
  • 网络状态监测: 结合 NetworkType.CONNECTED 约束,确保在无网络时不触发 Worker。
// 限制最大重试 5 次
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
    )
    .setInitialRunAttemptCount(5)
    .build()

4.8 完整 Worker 示例(含 Retrofit 调用)

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

    override fun doWork(): Result {
        val startTime = System.currentTimeMillis()

        return try {
            val apiService = RetrofitClient.create()
            val response = apiService.syncData(
                 = inputData.getString("auth_", ""),
                lastSyncTime = inputData.getLong("last_sync_time", 0L)
            ).execute()

            if (response.isSuccessful) {
                applyDataToLocal(response.body())
                Result.success()
            } else {
                // 服务器返回 4xx/5xx
                Result.failure()
            }
        } catch (e: IOException) {
            Log.w("SyncWorker", "网络异常,准备重试", e)
            Result.retry()
        } catch (e: Exception) {
            Log.e("SyncWorker", "同步失败", e)
            Result.failure()
        } finally {
            val duration = System.currentTimeMillis() - startTime
            Log.d("SyncWorker", "本次同步耗时: $duration ms")
        }
    }

    private fun applyDataToLocal(response: SyncResponse?) {
        // 将服务器返回的数据写入本地数据库(Room / SQLite)
    }
}