0x00 WorkManager的高級用法
在上一文中已經了解到 WorkManager的基本用法之后,今天來看看它的一些高級用法:
- 鏈式任務調用
- 唯一任務序列
- 傳遞參數和獲取返回值
0x01 鏈式任務(Chained tasks)
WorkManager在執行多個工作任務的時候,可以指定執行順序。假設一個應用程序中有3個 OneTimeWorkRequest對象: workA、 workB、 workC。這幾個任務需要按照順序執行,那么可以使用 WorkManager.beginWith()方法加入 workA,這時候會返回一個 WorkContinuation對象,它定義了工作任務的執行序列。然后通過它再調用 WorkContinuation.then()把 workB和 workC加入到執行隊列中,最后執行 WorkManager.enqueue()方法。
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會按照指定的順序來執行 workA、 workB、 workC。如果其中有一個工作任務執行的時候返回 Worker.Result.FAILURE,那么整個執行序列就會停止。
WorkManager.beginWith()方法可以傳遞多個 Worker對象,表示可以並行執行的任務組,然后再調用 then()方法。當這並行任務組都執行完之后才會執行接下來 then中的工作任務。
WorkManager.getInstance()
// 首先,並行執行workA1,workA2,workA3這三個任務
.beginWith(workA1, workA2, workA3)
// 當三個任務都完成之后, 開始執行workB:
.then(workB)
// 最后再並行執行 workC1,workC2
.then(workC1, workC2)
.enqueue()
還可以通過 WorkContinuation.combine()方法創建更加復雜的鏈接任務調用序列。它可以將兩個 WorkContinuation對象合並,假設要調用如下的任務序列:

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()
這個鏈式執行順序是:子鏈 A->B 與子鏈 C->D 並行執行的, workA執行后再執行 workB,以及 workC執行后執行 workD ;然后等待 workB以及 workD都執行完,最后執行 workE。
需要注意的是, WorkManager無法保證兩個子鏈的執行順序,有可能 chain1比 chain2快,也有可能是 chain1比 chain2慢。
0x02 唯一任務序列(Unique work sequences)
在應用程序開發中,可能會多次把同一個鏈式任務添加到 WorkManager中,但希望只有一個鏈式任務在執行,這時候可以使用唯一任務序列對鏈式任務指定處理規則。假設,做一個下載文件操作,對一個文件下載鏈接,我們不需要重復下載,只需要添加一次,后面再添加這個任務,就忽略掉,因為我們不希望重復多次下載同一個文件。所以當添加兩個同樣名稱為"download"操作任務時,對於唯一任務序列來說,可以通過 ExistingWorkPolicy中的 REPLACE, KEEP 和 APPEND,來指定添加的策略。
- REPLACE:新任務將替換舊的
- KEEP:新任務會被丟棄,舊的任務會被保持
- APPEND:追加,舊任務執行之后再執行新的任務。
使用 beginUniqueWork()方法可以創建任務序列,並且可以指定唯一的一個名稱(name)。然后再 ExistingWorkPolicy指定任務的替換策略
WorkContinuation continuation = mWorkManager
.beginUniqueWork("download",
ExistingWorkPolicy.KEEP,
OneTimeWorkRequest.from(CleanupWorker))
0x03 傳遞參數和獲取返回值
任務執行可以傳遞參數以及獲取到任務執行的結果。使用 WorkRequst.Builder.setInputData()方法傳遞一個 Data對象,它是key-value形式的對象,使用 Data.Builder來創建。在 Worker類中可以使用 Worker.getInputData()獲取到參數。
同樣地,在 Worker中可以使用 Worker.setOutputData()設置一個 Data對象的返回值。要獲取到這個返回值就通過 LiveData<WorkStatus>。
舉個栗子:
有一個下載任務,在 Worker中獲取傳遞過來的參數url,然后執行下載,最后設置下載結果。
// the result key:
const val KEY_RESULT = "result"
class DownloadWorker(context : Context, params : WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 獲取參數
val url = getInputData("url")
// 執行下載
val result = download(url);
// 設置下載結果
val output: Data = mapOf(KEY_RESULT to result).toWorkData()
setOutputData(output)
// 任務執行成功
return Result.SUCCESS
}
}
然后,通過 WorkRequest傳遞參數
// 構造下載鏈接參數
val urlData: Data = mapOf("url" to "https://developer.android.com/images/topic/libraries/architecture/workmanager-chain.svg")
.toWorkData()
// 構造WorkRequest並傳遞下載參數
val downloadWork = OneTimeWorkRequest.Builder<DownloadWorker>()
.setInputData(urlData)
.build()
// 交給WorkManager執行任務
WorkManager.getInstance().enqueue(downloadWork)
最后,通過 WorkStatus獲取返回值
WorkManager.getInstance().getStatusById(downloadWork.id)
.observe(this, Observer { status ->
if (status != null && status.state.isFinished) {
val myResult = status.outputData.getString(KEY_RESULT,
myDefaultValue)
// ... do something with the result ...
}
})
0x04 引用
https://developer.android.com/topic/libraries/architecture/workmanager/advanced
https://developer.android.com/reference/androidx/work/ExistingWorkPolicy
http://clmirror.storage.googleapis.com/codelabs/android-workmanager/index.html?index=..%2F..%2Findex#0
