[译] Android - Jetpack - Advanced WorkManager topics

翻译自
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() 方法通过连接多个链创建更复杂的序列,例如:假设你想运行下面这样的序列:

[译] Android - Jetpack - Advanced WorkManager topics_第1张图片
Figure1 用 WorkContinuation 建立复杂的链任务

为了设置这样的序列,需要创建俩个任务链,然后将他们连接到第三个链中:

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 中查看应用

学习中,翻译生硬或错误之处,还望指正

你可能感兴趣的:([译] Android - Jetpack - Advanced WorkManager topics)