3. 后台同步任务的生命周期管理:Service / WorkManager 选择、JobScheduler 与网络状态监听、后台执行限制适配

3.1 后台同步的生命周期挑战

Retrofit 网络同步任务通常需要在应用后台(即用户未直接与 Activity 交互)时继续执行。Android 系统对后台执行施加了严格的限制,主要包括:

  • Android 8.0 (API 26) 后台服务限制:应用在后台运行几分钟后,系统会停用其后台 Service。
  • Android 9 应用待机(App Standby):处于空闲状态的应用 CPU 和网络访问受限。
  • Android 12+ 精简待机(Battery Optimization):更激进的电源管理,后台同步可能被延迟或取消。

因此,依赖传统 Service 执行后台同步已经不可靠,必须采用系统推荐的 WorkManager 或 JobScheduler 等组件来管理生命周期。

重要提醒:在 Android 8+ 上,如果仍使用 startService() 在后台执行长时网络同步(超过几分钟),系统会抛出 IllegalStateException 或直接杀死进程。必须及时迁移到 WorkManager。

3.2 方案选择:Service vs WorkManager vs JobScheduler

特性传统 ServiceJobSchedulerWorkManager
最低 API121 (Lollipop)14 (配合兼容库)
后台执行保证无(Android 8+ 被杀死)系统会尽力执行,但不保证精确时间保证执行,即便应用被杀死或重启
网络约束需手动监听 ConnectivityManager内置 JobInfo.NETWORK_TYPE内置 NetworkType 约束
电源/电量约束无支持充电/空闲/电量支持充电、电池不低等
重试策略需自行实现支持基本重试(线性/指数退避)内置指数退避、最大重试次数
链式/并行困难较复杂原生支持链式及并行任务
推荐场景仅前台服务(前台通知)非关键、可延迟的一次性任务所有可延迟的后台同步(最佳实践)
结论:对于 Retrofit 后台同步,首选 WorkManager,它兼容旧版本、生命周期感知、内置重试与约束。仅在必须立即执行且需要前台通知时使用前台服务(Foreground Service)。

3.3 JobScheduler 与网络状态监听

如果不使用 WorkManager 而直接使用 JobScheduler,需要如下步骤:

  1. 构造 JobInfo,设置网络约束(如 NETWORK_TYPE_UNMETERED 或 NETWORK_TYPE_ANY)。
  2. 创建 JobService 子类,重写 onStartJob() 和 onStopJob()。
  3. 在 onStartJob() 中执行 Retrofit 同步,完成后调用 jobFinished()。
  4. 手动监听网络状态变化:注册 ConnectivityManager.NetworkCallback 以触发 Job 重新调度。

示例配置:

ComponentName component = new ComponentName(context, SyncJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, component)
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
        .setRequiresCharging(true)
        .setBackoffCriteria(30_000, JobInfo.BACKOFF_POLICY_EXPONENTIAL)
        .setMinimumLatency(60_000)
        .build();
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);
网络状态监听替代方案:如果仍需要更细粒度的网络变化回调(如从 WiFi 切换到蜂窝数据),可以在 JobService 中注册 NetworkCallback,但 WorkManager 已经内置了类似功能,避免重复造轮子。

3.4 后台执行限制适配(Android 8+ 及后续版本)

即使使用 WorkManager,也需要注意以下适配:

  • Android 8.0 后台服务限制:WorkManager 的 Worker 默认运行在系统管理的线程池中,不受 startService 限制。
  • Android 9 应用待机:若应用被标记为“空闲”,同步可能被延迟,WorkManager 会等待系统允许再执行。
  • Android 12+ 精确闹钟受限:如果同步需要精确时间,请使用 setExactAndAllowWhileIdle() 但优先考虑 WorkManager 的延迟容忍。
  • 电池优化白名单:对于极其关键的同步(如消息推送心跳),可引导用户加入电池优化白名单,但不推荐滥用。

WorkManager 自适应策略:它根据设备 API 级别自动选择底层调度器(JobScheduler / GcmNetworkManager / AlarmManager),开发者无需关心细节。

3.5 实现流程(WorkManager + Retrofit 网络约束同步)

  1. 定义 Worker:继承 Worker 或 CoroutineWorker,在 doWork() 中调用 Retrofit 同步请求。
  2. 构建约束:指定网络类型(如 NetworkType.CONNECTED 或 NetworkType.UNMETERED),并可选电量/充电/存储约束。
  3. 创建 WorkRequest:使用 OneTimeWorkRequestBuilder 或 PeriodicWorkRequestBuilder,设置约束及重试策略。
  4. 入队:通过 WorkManager.getInstance(context).enqueue() 提交。
  5. 观察结果:通过 LiveData 或 ListenableFuture 获取执行状态。

核心代码:

// 1. 定义 Worker (Kotlin Coroutine)
class SyncWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        return try {
            val response = RetrofitClient.apiService.syncData()
            if (response.isSuccessful) {
                // 处理成功
                Result.success()
            } else {
                Result.retry()
            }
        } catch (e: Exception) {
            if (runAttemptCount < 3) Result.retry() else Result.failure()
        }
    }
}

// 2. 构建约束和 WorkRequest
val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED)  // 有网络即可
    .setRequiresBatteryNotLow(true)
    .build()

val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
    .setConstraints(constraints)
    .setBackoffCriteria(
        BackoffPolicy.EXPONENTIAL,
        WorkRequest.MIN_BACKOFF_MILLIS,
        TimeUnit.MILLISECONDS
    )
    .build()

// 3. 入队
WorkManager.getInstance(context).enqueue(syncRequest)

3.6 调试与部署

阶段方法
调试 WorkManager 任务使用 WorkManager.getInstance().getWorkInfoByIdLiveData() 观察状态,或使用 adb shell dumpsys jobscheduler 查看 Job 调度情况。
强制触发任务命令行:
adb shell am broadcast -a "androidx.work.diagnostics.REQUEST_DIAGNOSTICS" && adb shell dumpsys activity service androidx.work.impl.background.systemjob.SystemJobService
模拟网络约束使用 Android 模拟器的“设置 > 开发者选项 > 网络”调整网络类型,或通过 adb shell cmd connectivity airplane-mode enable/disable。
后台执行限制测试在 Android 8+ 模拟器上,将应用推入后台,等待 5 分钟后观察 Service 是否被杀;对于 WorkManager,任务应在约束满足后正常执行。
稳定性监控在 Worker 内添加日志并上报到 Crashlytics;在 doWork() 中捕获所有异常,避免未捕获异常导致任务永久失败。

3.7 常见错误与性能优化

错误1:IllegalStateException: Not allowed to start service Intent
原因:在后台尝试启动 Service。
解决:改用 WorkManager,或将 Service 提升为前台服务(startForeground())。
错误2:WorkManager 任务未按预期触发。
排查步骤:检查约束是否过高(如“充电中”且“电量不低”同时设置);检查是否设置了最小延迟但系统延迟了;查看 adb shell dumpsys jobscheduler 中该 Job 的状态是否为“waiting”。
性能优化:
  • 尽量使用 NetworkType.CONNECTED 而非 UNMETERED,以在移动网络下也能同步(若数据量小且用户不介意)。
  • 重试策略使用指数退避,默认最小间隔为 10 秒(MIN_BACKOFF_MILLIS),可根据需要调整为 30 秒。
  • 避免在 Worker 中持有 Context 引用超过 doWork() 生命周期,防止内存泄露。
  • 如果同步数据量较大,建议分页并搭配 NetworkType.UNMETERED 和 setRequiresCharging(true),减少用户流量和电池消耗。

3.8 生命周期管理流程图

以下示意图展示了基于 WorkManager 的后台同步任务从创建到执行完成的生命周期,以及系统约束检查点:

后台同步生命周期管理决策流(WorkManager 方案) 应用触发同步 构建 WorkRequest + 约束 WorkManager.enqueue() 系统检查约束 等待约束满足 Worker.doWork() 执行 Result.success() 重试 系统自动处理重试延迟 (指数退避)