3. 链式任务的基本创建:beginWith 与 then 方法

3.1 beginWith / then 顺序执行原理

WorkManager 通过 WorkContinuation 对象实现严格的顺序执行。调用 beginWith() 会创建一个新的链起点,返回 WorkContinuation 实例;每次调用 .then() 都会基于前一个 WorkContinuation 生成一个新的 WorkContinuation,并将新任务注册为前驱任务的子任务。

内部采用 DAG(有向无环图) 管理依赖:只有当链中所有前置任务都返回 Result.success() 后,后续任务才会被触发执行。任意一个节点返回 Result.failure(),整条链将终止并标记为失败。

核心原则:

  • beginWith() 只能接收 OneTimeWorkRequest 或 List<OneTimeWorkRequest>,不可传入 PeriodicWorkRequest。
  • .then() 同样只支持一次性任务,且必须依赖前置 WorkContinuation 调用。
  • 链式调用必须调用 .enqueue() 才会实际入队执行。

3.2 API 签名与返回值对比

方法 输入参数 返回值 说明
beginWith() OneTimeWorkRequest 或 List<OneTimeWorkRequest> WorkContinuation 创建链式调度起点;可传入多个请求实现头部并行
.then() OneTimeWorkRequest 或 List<OneTimeWorkRequest> WorkContinuation 追加到现有链尾部;依赖链中所有前置任务的成功

3.3 实现流程:构建三阶段处理管道

以下示例展示一个典型的图像处理场景:下载 → 滤镜 → 上传,三个 Worker 通过链式调度按序执行。

// Java 示例
OneTimeWorkRequest downloadWork = new OneTimeWorkRequest.Builder(DownloadWorker.class)
        .setConstraints(new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build())
        .addTag("download")
        .build();

OneTimeWorkRequest filterWork = new OneTimeWorkRequest.Builder(FilterWorker.class)
        .addTag("filter")
        .build();

OneTimeWorkRequest uploadWork = new OneTimeWorkRequest.Builder(UploadWorker.class)
        .addTag("upload")
        .build();

WorkManager.getInstance(context)
        .beginWith(downloadWork)
        .then(filterWork)
        .then(uploadWork)
        .enqueue();
// Kotlin 示例
val downloadWork = OneTimeWorkRequestBuilder<DownloadWorker>()
    .setConstraints(Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build())
    .addTag("download")
    .build()

val filterWork = OneTimeWorkRequestBuilder<FilterWorker>()
    .addTag("filter")
    .build()

val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
    .addTag("upload")
    .build()

WorkManager.getInstance(context)
    .beginWith(downloadWork)
    .then(filterWork)
    .then(uploadWork)
    .enqueue()

3.4 任务依赖关系与数据传递

链式任务中,后置 Worker 可以通过 getInputData() 获取前置 Worker 输出的数据。WorkManager 默认使用 OverwritingInputMerger 合并键值对,若需保留所有历史数据,可显式指定 ArrayCreatingInputMerger。

// DownloadWorker 输出
Data output = new Data.Builder()
        .putString("image_uri", resultUri)
        .build();
return Result.success(output);

// FilterWorker 接收
Data input = getInputData();
String imageUri = input.getString("image_uri");

调试技巧: 使用 WorkManager.getWorkInfoByIdLiveData(workRequest.id) 观察每个任务的状态转换:ENQUEUED → RUNNING → SUCCEEDED。若某任务失败,后续任务状态将直接跳转为 BLOCKED。

3.5 常见错误与稳定性优化

  • 死锁风险: 避免在 .then() 中传入依赖于当前链其他分支未完成的任务请求,否则会导致 ILLEGAL_STATE 异常。
  • 内存泄漏: 在 Activity/Fragment 中观察 WorkInfo 时,注意使用 LifecycleOwner 绑定生命周期,或调用 .observeForever() 后及时移除观察者。
  • 约束条件冲突: 如果链中某个任务设置了严格的约束(如充电状态),即使前置任务成功,该任务也会等待约束满足后再执行,这可能导致整条链延后。

性能注意: 避免在单条链中串联超过 10 个任务。过长的链会延长整体执行时间,降低系统调度灵活性。建议将长链拆分为多条独立子链,并通过 WorkContinuation.combine() 合并结果。

3.6 链式任务调度流程图

下图清晰展示 beginWith 与 then 的完整调用链路及依赖关系:

beginWith / then 链式任务创建与执行流程 beginWith(A) .then(B) .then(C) .enqueue() 任务依赖关系与顺序执行规则 任务A执行成功 → 任务B开始执行(可访问A的输出数据) 任务B执行成功 → 任务C开始执行(可访问A和B的输出数据) 链中任何一个任务失败,后续任务将被跳过,整个链标记为失败

3.7 状态观察与结果验证

通过 WorkManager.getWorkInfosByTagLiveData() 可批量观察链中任务状态,推荐在 ViewModel 中订阅,避免屏幕旋转导致重复注册。

// 观察 download 标签下所有任务的状态
WorkManager.getInstance(context)
    .getWorkInfosByTagLiveData("download")
    .observe(getViewLifecycleOwner(), workInfos -> {
        for (WorkInfo workInfo : workInfos) {
            Log.d("WorkManager", "Task: " + workInfo.getId() + 
                  " State: " + workInfo.getState().name());
            if (workInfo.getState() == WorkInfo.State.FAILED) {
                // 处理失败逻辑,例如重试或通知用户
            }
        }
    });

关键提示: 链式任务的 id 是唯一的;可通过 beginWith 返回的 WorkContinuation 获取最终任务的 id,用于精确监听整条链的完成状态。