如何使用WorkManager執行後臺任務(上)

hylinux1024發表於2018-10-01

0x00 簡述

WorkManager 是 Android Jetpack中的一部分,它主要是封裝了 Android 後臺任務的排程邏輯。在前文《Android後臺任務處理指南》一文中知道,WorkManager 是高階 API,它實際是封裝了JobScheduler, Firebase JobDispatcher, 和 AlarmManager 底層的使用,提供了簡單且靈活易用的API,它有很多優勢:

  • 支援非同步一次性或週期任務

  • 支援網路、儲存空間以及電量狀態等約束

  • 可使用鏈式的呼叫方式來執行任務,也包括並行任務處理

  • 一個工作任務的執行結果可以作為下一個任務的輸入

  • 相容API 14 以上

  • 可以支援Google play services

  • 支援LiveData

0x01 理論篇——重要的類

WorkManager庫中有一些非常重要的類,這些類幫助你構建後臺執行的工作任務:

  • Worker:這是一個抽象類,它表示一個工作任務,繼承這個類實現doWork()方法,這裡就是實現執行任務的主要邏輯。

  • WorkRequest:表示一個工作任務請求,指定執行哪個Work,它還可以設定任務執行的約束條件。每一個WorkRequest都有一個自動生成的唯一ID。使用這個ID可以取消任務的執行,或者取得當前任務的執行狀態。WorkRequest也是一個抽象類,使用的時候是框架庫中提供的子類OneTimeWorkRequestPeriodicWorkRequest類,分別代表一次性任務和週期任務

    • WorkRequest.Builder:這是一個建立工作任務請求的輔助工具類。它有兩個具體的實現:OneTimeWorkRequest.Builder類和PeriodicWorkRequest.Builder類,可以分別建立相應的任務請求例項。

    • Constraints:它表示工作任務執行的約束條件。指的是在哪一些系統條件下(例如只有網路連線情況下),這個工作任務會被執行。同樣地,建立Constraints也有Builder方法:Constraints.Builder。約束類是可以在使用WorkRequest.Builder 建立的時候進行傳遞給WorkRequest

  • WorkManager:工作工作管理員,它是用於管理工作任務的請求佇列的類。通過它可以把一個WorkRequest加入到任務佇列中,然後根據系統的資源和約束條件對工作任務進行排程。

  • WorkStatus:工作狀態類,它包含了工作任務當前的狀態資訊。WorkManager 為每一個WorkRequest都提供了LiveData物件,而LiveData物件又持有WorkStatus資訊,因此通過LiveData可以監聽到當前任務的狀態資訊,並且獲取到任務結束後的執行結果。

通過上面的描述,可以簡單的畫出下面的關係圖:

0x02 實踐篇——工作流

假設我們在開發一個圖片相關的APP,這個應用需要期地壓縮它儲存的圖片。我們使用WorkManager來實現這個需求。這種情況,我們不關心壓縮任務什麼時候開始,只要開啟一個壓縮任務,其他的就交給WorkManager了。

  1. 定義Worker

我們首先要定義一個Worker類,並重寫doWork()方法,這裡定義瞭如何執行任務的具體邏輯。例如在此例子中,myCompress()就是具體的執行任務的邏輯。

class CompressWorker(context : Context, params : WorkerParameters)
    : Worker(context, params) {

    override fun doWork(): Result {
        // Do the work here--in this case, compress the stored images.
        // In this example no parameters are passed; the task is
        // assumed to be "compress the whole library."
        myCompress()

        // Indicate success or failure with your return value:
        return Result.SUCCESS

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}
 

doWork() 執行後還可以返回執行的結果:Result.SUCCESSResult.FAILUREResult.RETRY 分別表示成功失敗重試

  1. 建立WorkRequest

使用WorkRequest的子類,根據具體業務建立對應的請求例項。例如本例中,使用一次性任務來構建請求。然後把任務請求加入到WorkManager管理的佇列中。


val compressionWork = OneTimeWorkRequest.Builder<CompressWorker>().build()
WorkManager.getInstance().enqueue(compressionWork)
 

這樣這個任務就交給WorkManager了,任務管理者根據當前系統情況(是否充電、網路狀態等等)對任務進行排程。如果沒有給WorkRequest指定約束條件,那麼這個任務會馬上執行。如果需要得到這個任務的狀態,可以通過LiveData<WorkStatus>來監聽。

WorkManager.getInstance().getStatusById(compressionWork.id)
                .observe(lifecycleOwner, Observer { workStatus ->
                    // Do something with the status
                    if (workStatus != null && workStatus.state.isFinished) {
                        // ...
                    }
                })

 

  1. 任務約束

可以給一個任務指定約束條件。例如,可以在裝置空閒的時候、或者充電的時候執行任務。這種情況,可以使用Constraints.Builder來建立一個約束條件例項,然後傳遞到WorkRequest中。


// Create a Constraints object that defines when the task should run
val myConstraints = Constraints.Builder()
        .setRequiresDeviceIdle(true)
        .setRequiresCharging(true)
        // Many other constraints are available, see the
        // Constraints.Builder reference
        .build()

// ...then create a OneTimeWorkRequest that uses those constraints
val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>()
        .setConstraints(myConstraints)
        .build()

 

最後,還是呼叫WorkManagerenqueue方法將WorkRequest加入佇列。這時候,WorkManager在執行任務的時候就會考慮約束條件的情況。

  1. 取消任務

取消任務需要一個任務ID,可以通過WorkRequest來獲取。然後呼叫WorkManagercancelWorkById方法來取消任務的執行。


val compressionWorkId:UUID = compressionWork.getId()
WorkManager.getInstance().cancelWorkById(compressionWorkId)
  1. 給任務設定Tag

可以給一組任務設定一個Tag。在建立 WorkRequest 的時候,可以執行給它指定一個Tag;而另一個任務請求也可以設定相同的Tag。

val cacheCleanupTask =
        OneTimeWorkRequest.Builder<MyCacheCleanupWorker>()
    .addTag("one-tag")
    .build()
val loadImageTask = OneTimeWorkRequest.Builder<MyImageLoadWorker>()
    .addTag("one-tag")
    .build()
 

WorkManager提供了很多有用的API操作這些相同Tag的任務。例如,WorkManager.cancelAllWorkByTag() 可以取消具有相同Tag的一組任務;還可以通過WorkManager.getStatusesByTag()獲取一組任務的狀態列表。

  1. 週期任務

有些業務場景是需要重複執行一個任務的。例如,在一個圖片應用中,可能會有一個定期檢查需要壓縮圖片的任務。這時候可使用到 PeriodicWorkRequest.Builder 來建立一個週期任務。跟一次性任務一樣,通過WorkManager.enqueue() 方法加入到工作任務佇列中。


val photoCheckBuilder =
        PeriodicWorkRequest.Builder<PhotoCheckWorker>(12, TimeUnit.HOURS)
// ...if you want, you can apply constraints to the builder here...

// Create the actual work object:
val photoCheckWork = photoCheckBuilder.build()
// Then enqueue the recurring task:
WorkManager.getInstance().enqueue(photoCheckWork)

 

接下來的事情就交給 WorkManager 了。

0x03 引用

https://developer.android.com/topic/libraries/architecture/workmanager/basics


相關文章