如何使用WorkManager執行後臺任務(下)
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