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 的完整调用链路及依赖关系:
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,用于精确监听整条链的完成状态。