Android Architecture Component WorkManager 高级(翻译)

Android Architecture Component WorkManager 高级(翻译)

原文:https://developer.android.com/topic/libraries/architecture/workmanager/advanced

通过 WorkManager 可以轻松地设置和安排复杂的任务请求。你可以在以下场景使用这些 APIs:

  • 通过指定顺序去执行 tasks 的 链序列
  • 唯一的 命名序列,以及在应用启动两个同名序列时发生的情况的规则。
  • Tasks 的 输入输出值,包括每个 task 的输出作为参数输入到下一个 tasks 中的链 tasks。

链 tasks

你的 app 可能需要以一个特定的顺序来执行多个 tasks。WorkManager 允许你创建一个工作序列并对它进行排队,该序列指定多个 tasks 以及它应该按什么顺序运行。

举个例子,假设你的 app 有三个 OneTimeWorkRequest 对象:workAworkBworkC。任务必须要按照这个顺序执行。为了排队它们,要通过 WorkManager.beginWith()) 方法创建一个序列,传入第一个 OneTimeWorkRequest 对象;这个方法返回一个 WorkContinuation 对象,它定义了 tasks 的序列。然后使用 WorkContinuation.then() 按照顺序增加其它的 OneTimeWorkRequest 对象,最后,使用 WorkContinuation.enqueue() 来排队整个序列:

1
2
3
4
5
6
7
8
WorkManager.getInstance()
.beginWith(workA)
// Note: WorkManager.beginWith() returns a
// WorkContinuation object; the following calls are
// to WorkContinuation methods
.then(workB) // FYI, then() returns a new WorkContinuation instance
.then(workC)
.enqueue()

WorkManager 会按照请求的顺序以及每个 task 指定的约束来运行 tasks。如果 任何 task 返回了 Worker.Result.FAILURE,则整个序列结束。

你也可以传入多个 OneTimeWorkRequest 对象到 beginWith()) 和 .then() 调用。如果你传入几个 OneTimeWorkRequest 到一个单独的方法调用,WorkManager 会在运行其余 tasks 之前运行这些 tasks(并行)。比如:

1
2
3
4
5
6
7
8
WorkManager.getInstance()
// 首先,运行所有 A tasks (并行):
.beginWith(workA1, workA2, workA3)
// ...当所有的 A tasks 结束, 运行单个 B task:
.then(workB)
// ...然后运行 C tasks (以任何顺序):
.then(workC1, workC2)
.enqueue()

你可以通过 WorkContinuation.combine() 方法连接多个链来创建更复杂的序列。比如,假设你想要运行像以下的序列:



要设置这个序列,创建两个独立的链,然后把它们链接在一起形成第三个:

1
2
3
4
5
6
7
8
9
10
val chain1 = WorkManager.getInstance()
.beginWith(workA)
.then(workB)
val chain2 = WorkManager.getInstance()
.beginWith(workC)
.then(workD)
val chain3 = WorkContinuation
.combine(chain1, chain2)
.then(workE)
chain3.enqueue()

在这种情况下,WorkManagerworkB 之前运行 workA。运行 workD 之前运行 workC。在 workBworkD 都完成之后,WorkManager 运行 workE

注意:当 WorkManager 按照顺序运行没个子链,不能保证 链1 中的任务与 链2 中的任务可能重叠。比如,workB 可能在 workC 之前或者之后运行,或者同时运行。只能保证每个子链中的 task 将按顺序运行;就是,workB 不会运行直到 workA 完成。

WorkContinuation 有许多变种来提供为特定情况提供快捷。举个例子,WorkContinuation.combine(OneTimeWorkRequest, WorkContinuation…)) 方法是指示 WorkManager 完成所有指定的 WorkContinuation 链,然后完成指定的 OneTimeWorkRequest。详请可参阅 WorkContinuation

唯一工作序列

你可以通过调用 beginUniqueWork()) 而不是 beginWith()) 开始一个序列来创建一个 唯一的工作序列。每一个唯一工作序列都有一个名字;WorkManager 一次只允许一个序列具有该名字。当你创建一个新的唯一工作序列时,你要指定如果已经有一个相同名字并且未完成的序列时 WorkManager 应该怎么做:

  • 取消已经存在的序列并用新的 替换
  • 保留 存在的序列并忽略掉你新的请求。
  • 追加 你新的序列到你存在的序列上,在存在的序列的最后一个 task 执行完之后执行新的序列的第一个 task。

唯一工作序列在你有一个 task 不该排队多次的时候很有用。举个例子,如果你的 app 需要同步数据到网络,你可能对名为 “sync” 的序列进行排队,然后指定如果已经有这个名字的序列存在,则你新的 task 应该被忽略。唯一工作序列在你需要去逐渐建立一个长长的任务链时也很有用。举个例子,一个图片编辑 app 可能会让用户撤销一个很长的动作链。这些每个撤销的操作都可能会需要花费一些时间,但是它们需要以一个正确的顺序被执行。在这个情况下,app 应该创建一个 “undo” 链,并且如果需要则追加每个撤销操作。

输入和输出值

为了更大的灵活性,你可以传入参数到你的 tasks 并让 task 返回值。输入输出值是键值对。要传入值到一个 task,则需要在你创建 WorkRequest 对象之前调用 WorkRequest.Builder.setInputData() 方法。这个方法接收一个 Data 对象,你可以通过 Data.Builder 来创建它。Worker 类可以通过 Worker.getInputData() 来访问这些参数。要输出一个返回值,task 要调用 Worker.setOutputData(),它接受一个 Data 对象。你可以通过观察 task 的 LiveData<WorkStatus> 来得到输出。

举个例子,假设你有一个 Worker 类来执行耗时的计算。以下代码展示了 Worker 类应该是怎么样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Define the parameter keys:
const val KEY_X_ARG = "X"
const val KEY_Y_ARG = "Y"
const val KEY_Z_ARG = "Z"

// ...and the result key:
const val KEY_RESULT = "result"

// Define the Worker class:
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)

// ...do the math...
val result = myCrazyMathFunction(x, y, z);

//...set the output, and we're done!
val output: Data = mapOf(KEY_RESULT to result).toWorkData()
setOutputData(output)

return Result.SUCCESS
}
}

要创建一个 worker 并传入参数,你应该如下编写代码:

1
2
3
4
5
6
7
8
9
10
val myData: Data = mapOf("KEY_X_ARG" to 42,
"KEY_Y_ARG" to 421,
"KEY_Z_ARG" to 8675309)
.toWorkData()

// ...then create and enqueue a OneTimeWorkRequest that uses those arguments
val mathWork = OneTimeWorkRequestBuilder<MathWorker>()
.setInputData(myData)
.build()
WorkManager.getInstance().enqueue(mathWork)

返回值会在 task 的 WorkStatus 中可用:

1
2
3
4
5
6
7
8
WorkManager.getInstance().getStatusById(mathWork.id)
.observe(this, Observer { status ->
if (status != null && status.state.isFinished) {
val myResult = status.outputData.getInt(KEY_RESULT,
myDefaultValue)
// ... do something with the result ...
}
})

如果你链接了 tasks,一个 task 的输出可作为链中下一个 task 的输入。如果它是一个简单链,使用单个的 OneTimeWorkRequest 链接另一个单个 OneTimeWorkRequest,第一个 tasker 通过调用 setOutputData()) 返回结果,然后下一个 task 通过调用 getInputData() 获取结果。如果链更复杂 —— 比如,因为几个 task 都发送输入到一个单个的后续任务 —— 你可以在 OneTimeWorkRequest.Builder 定义一个 InputMerger 来指定如果多个 tasks 返回一个相同 key 的输出时应该怎么处理。

额外的资源

WorkManager 是一个 Android Jetpack architecture component。可参阅使用到它的 Sunflower demo app。



来源博客:Wang Jie's Blog's Blog
本文链接:https://blog.wangjiegulu.com/2018/10/20/android_architecture_components_workermanager_advanced/
版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处。