任务链与并行任务:让WorkManager真正“串”起来
说实话,刚开始用WorkManager的时候,我总觉得它就是个“高级版的后台任务管理器”。直到有一天,我在做一个电商App的订单同步功能——需要先下载订单数据,再解析,再上传到本地数据库,最后通知UI更新。这一串操作如果拆成一个个独立任务,代码会变得又臭又长。嗯,这时候任务链就派上用场了。
今天我们就来聊聊WorkManager里最核心的编排能力:任务链(WorkChain)和并行任务组合。说白了,就是让你能像搭积木一样,把多个任务串起来或者并起来跑。
1. beginWith()与then():最简单的任务链
先看最基础的情况。假设你有三个任务:A、B、C,必须按顺序执行。A做完才能做B,B做完才能做C。这在WorkManager里怎么表达?
val workA = OneTimeWorkRequestBuilder<DownloadWorker>()
.build()
val workB = OneTimeWorkRequestBuilder<ParseWorker>()
.build()
val workC = OneTimeWorkRequestBuilder<SaveWorker>()
.build()
WorkManager.getInstance(context)
.beginWith(workA)
.then(workB)
.then(workC)
.enqueue()
这段代码看着是不是很直观?beginWith()启动链条,then()追加后续任务。我个人的习惯是,把beginWith()想象成“起跑线”,后面的then()就是接力棒。每个任务完成后,WorkManager会自动把结果传递给下一个。
2. 并行任务组合:WorkContinuation
但现实世界哪有这么简单?很多时候,我们需要同时做几件事。比如用户上传图片时,同时压缩图片、同时生成缩略图、同时上传到CDN。这三件事互不依赖,完全可以并行。
WorkManager提供了WorkContinuation来组合并行任务。看代码:
val compress = OneTimeWorkRequestBuilder<CompressWorker>()
.build()
val thumbnail = OneTimeWorkRequestBuilder<ThumbnailWorker>()
.build()
val upload = OneTimeWorkRequestBuilder<UploadWorker>()
.build()
// 三个任务并行执行
val parallelWorks = WorkContinuation.combine(
listOf(compress, thumbnail, upload)
)
parallelWorks.enqueue()
这里WorkContinuation.combine()接收一个WorkRequest列表,所有任务会同时启动。我在项目中遇到过一个问题:如果列表里有某个任务特别慢,整个combine会等它完成才继续。所以建议把耗时相近的任务放在一起,避免“木桶效应”。
3. 合并多个任务链
有时候,你会有多个独立的链条,最后需要合并成一个。举个例子:
- 链条1:下载用户信息 → 解析用户信息
- 链条2:下载用户头像 → 裁剪头像
- 最后:合并用户信息和头像 → 保存到本地
这种场景下,WorkContinuation.combine()可以接收多个WorkContinuation对象:
val chain1 = WorkManager.getInstance(context)
.beginWith(downloadUserInfo)
.then(parseUserInfo)
val chain2 = WorkManager.getInstance(context)
.beginWith(downloadAvatar)
.then(cropAvatar)
// 合并两个链条
val merged = WorkContinuation.combine(
listOf(chain1, chain2)
).then(saveToLocal)
merged.enqueue()
你看,combine()的参数可以是WorkContinuation列表,而不仅仅是WorkRequest列表。这样就能把任意复杂的链条组合起来。我曾经在做一个社交App的“发布动态”功能时,就用这个模式把图片处理、文字审核、位置获取三个链条合并,最后统一提交。
4. 任务链中的输入输出传递
任务链最强大的地方,就是数据可以在任务之间传递。每个Worker可以通过inputData接收数据,通过outputData输出数据。下一个任务会自动拿到上一个任务的输出作为输入的一部分。
看一个完整的例子:
// 任务A:下载订单数据
class DownloadOrderWorker(context: Context, params: WorkerParameters)
: Worker(context, params) {
override fun doWork(): Result {
val orderId = inputData.getString("order_id") ?: return Result.failure()
// 模拟下载
val rawData = "order_$orderId:item1,item2"
val output = workDataOf("raw_data" to rawData)
return Result.success(output)
}
}
// 任务B:解析订单数据
class ParseOrderWorker(context: Context, params: WorkerParameters)
: Worker(context, params) {
override fun doWork(): Result {
val rawData = inputData.getString("raw_data") ?: return Result.failure()
val items = rawData.split(":")[1].split(",")
val output = workDataOf("items" to items.toTypedArray())
return Result.success(output)
}
}
// 构建链条
val download = OneTimeWorkRequestBuilder<DownloadOrderWorker>()
.setInputData(workDataOf("order_id" to "12345"))
.build()
val parse = OneTimeWorkRequestBuilder<ParseOrderWorker>()
.build()
WorkManager.getInstance(context)
.beginWith(download)
.then(parse)
.enqueue()
这里的关键是:DownloadOrderWorker输出的raw_data,会自动成为ParseOrderWorker的输入。你不需要手动传递,WorkManager帮你做了。
5. 知识体系总览
为了让你更直观地理解任务链和并行任务的关系,我画了一张图:
这张图展示了三种核心模式:串行链条(A→B→C)、并行任务(X、Y、Z同时执行后合并)、以及链条合并(两个独立链条最后汇合)。你可以把它当作一个速查表,写代码时对照着看。
6. 总结一下
任务链和并行任务,说白了就是WorkManager给你的“编排工具箱”。我个人觉得,掌握这三个要点就够了:
- 串行用then():一个接一个,数据自动传递
- 并行用combine():多个任务同时跑,等所有完成再继续
- 数据用input/output:别偷懒用全局变量,Worker的生命周期你控制不了
嗯,这些基础概念搞懂了,后面我们才能聊更高级的话题——比如任务链的监听、唯一工作链、以及如何与LiveData/Flow结合。一步一步来,别急。