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

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<WorkStatus> 获取输出结果。

例如:假设你有一个 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<MathWorker>()
                  .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 中查看应用

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343