翻译自
Advanced WorkManager topics
WorkManager可以轻松设置和安排缜密的任务请求,可以将APIs用于以下方案:
- 以指定顺序运行的链式任务序列
- 唯一的命名序列,如果启动俩个相同名称的序列,将发生什么
- 传递和返回值的任务,包括每个传递参数到链中的下一个任务的链式任务
链式任务
您的应用可能需要按照特定的顺序运行多个任务。WorkManager 允许你创建一个指定的多任务队列,并把它放入该队列中按应该的顺序执行。
例如,假设你的应用由三个 OneTimeWorkRequest 对象:workA,workB和 workC。这些任务必须按照这个顺序运行。为了把他们放入队列,用 WorkManager.beginWIth() 方法创建一个序列,传递第一个 OneTimeWorkRequest 对象,这个方法返回了一个 WorkContinuation 对象,它定义了一个任务序列。然后用 WorkContinuation.then() 添加剩余 OneTimeWorkRequest 对象,最后用 WorkContinuation.enqueue() 将整个序列排入队列
WorkManager.getInstance()
.beginWith(workA)
.then(workB)
.then(workC)
.enqueue()
WorkManager 根据每个任务指定的约束,按照请求的顺序运行任务。如果任何任务返回 Worker.Result.FAILURE,这整个序列结束。
你也可以传递多个 OneTimeWorkRequest 对象传递个任何 beginWith() 和 .then()调用。如果你传递多个 OneTimeWorkRequest 对象传递给耽搁方法调用,WorkManager 会在运行队列剩余的任务前运行多有这些任务(并行),例如:
WokrManager.getInstance()
.beginWith(workA1, workA2, workA3)
.then(workB)
.then(workC1, workC2)
.enqueue()
你可以用 WorkContinuation.combine() 方法通过连接多个链创建更复杂的序列,例如:假设你想运行下面这样的序列:
为了设置这样的序列,需要创建俩个任务链,然后将他们连接到第三个链中:
val chain1 = WorkManager.getInstance()
.beginWith(workA)
.then(workB)
val chain2 = WorkManager.getInstance()
.beginWith(workC)
then(workD)
val chain3 = WorkManager.getInstance()
.combine(chain1, chain2)
.then(workE)
chain3.enqueue()
在这个例子中,WorkManager 先运行 workA 后运行 workB。先运行 workC 后运行 workD。当 workB 和 workD 运行结束后,WorkManager 再运行 workE。
Note:当 WorkManager 按顺序运行每一个子任务链时,不能保证 chain1中的任务和 chain2中的任务重叠,例如,workB 可能在 workC 之前或者之后,或者在相同的时间运行。只能保证每个子任务链将按顺序执行,即 workB 绝不会在 workA 之前执行直到它运行结束。
WorkContinuation 有很多扩展方法为特殊情况提供方便。例如 WorkContinuation.combine(OneTimeWorkRequest, WorkContiuation...)
方法,它说明WorkManager完成全部指定的 WorkContinuation 链,然后完成指定的 OneTimeWorkRequest,更多详情参考 WorkContinuation
唯一的任务序列
你可以创建唯一任务序列,通过调用 beginUniqueWork()代替 beginWith()开始序列。每一个唯一的序列有一个名字。WorkManager 一次仅仅允许一个该名称的工作序列。创建一个唯一的工作序列时,如果已经存在一个具有相同名称的未完成序列,你需要指定 WorkManager 应该怎么做:
- 取消已经存在的,用新的代替
- 采用已经存在的,忽略新的请求
- 将新的序列追加到已经存在的序列,在现有序列的最后一个任务完成后运行新序列的第一个任务
如果你的任务不应该多次入队,那么唯一工作序列是非常有用的。例如:你的应用需要和网络同步数据,你可以将一个名叫“sync”的序列入队,如果已经存在一个相同名字的队列,你需要指定忽略新的任务。
如果你需要逐步建立一个长任务,那么唯一工作序列也是非常有用的。例如:一个编辑照片的应用可能让用户取消一系列行为,每一个撤销操作可能需要一些时间,但是我们不得不按照正确的顺序执行。在这种情况下,可以穿件一个名叫“undo”的任务链,当需要时向其中追加撤销任务。
输入参数和返回值
为了获得更大的灵活性,你能向你的任务传递参数并且让任务返回结果。传递和返回的值时键值对。
向任务传递参数,请在创建 WorkRequest 对象以前调用 WorkRequest.Builder.seInputData() 方法。该方法采用 Data 对象,你能使用 Data.Builder创建它。Worker 类能通过调用 Work.getInputData() 访问这些参数。
输出返回值,任务可以调用 Worker.setOutputData(),它采用 Data 对象,你能通过观察任务的 LiveData
例如:假设你有一个 Worker 类执行耗时的计算。下面的代码展示了 Worker 类怎样编写:
const val KEY_X_ARG = "X"
const val KEY_Y_ARG = "Y"
const val KEY_Z_ARG = "Z"
const val KEY_RESULT = "result"
class MathWorker(context : Context, params : WorkerParameters)
: Worker(context, params) {
override fun doWork() : Result {
val x = inputData.getInt(KEY_X_ARG, 0)
val y = inputData.getInt(KEY_Y_ARG, 0)
val z = inputData.getInt(KEY_Z_ARG, 0)
val result = myCrazyMathFunction(x, y, z)
val output : Data = mapOf(KEY_RESULT to result).toWorkData()
setOutputData(output)
return Result.SUCCESS
}
}
创建 Work 并且传递参数,你可以这样写代码:
val myData : Data = mapOf("KEY_X_ARG" to 42,
"KEY_Y_ARG" to 421,
"KEY_Z_ARG" to 86768,)
.toWorkData()
val mathWork = OneTimeRequestBuilder()
.setInputData(myData)
.build()
WorkManager.getInstance().enqueue(mathWork)
在任务的 WorkStatus 中获得返回值
WorkManager.getInstance().getStatusById(mathWork.id)
.observe(this, Observer { status ->
if(status!=null && status.state.isFinished) {
val myResult = status.outputData.getInt(KEY_RESULT, myDefaultValue)
}
})
如果是链任务,一个任务的输出作为下一个任务的输入。如果它是一个简单的任务链,一个 OneTimeWorkRequest跟着另一个 OneTimeWorkRequest,第一个任务通过调用 setOutputData() 返回结果,下一个任务通过调用 getInputData() 获取结果。如果是一个复杂的任务链,例如多个任务发送输出值到一个任务,你能在 OneTimeWorkRequest.Builder 定义一个 InputMerger,如果不同任务返回同一个key的输出指定怎么处理。
额外的资源
WorkManager 是一个 Android Jetpack架构组件,可以在 Sunflower 的 Demo 中查看应用
学习中,翻译生硬或错误之处,还望指正