天天看點

Android Jetpack - 使用WorkManager處理簡單的背景任務

阿裡P7移動網際網路架構師進階視訊(每日更新中)免費學習請點選: https://space.bilibili.com/474380680 當我們讨論背景處理任務的時候,一般可能涉及的行為類型有下面一些類型,例如:

  • 發送程式運作日志
  • 上傳圖檔和視訊
  • 同步資料
  • 處理資料

這些行為都需要在背景進行操作,在Android平台上,我們可以利用如下的這些可選方式來實作背景任務:

那麼到底我們如何做出合理的選擇呢?過去的幾年,Android系統随着版本的更新針對電量優化這一塊做出了不同程度的限制優化,例如在Android M上的

Doze Mode

,Android N上的

Limit Implicit Broadcast

,Android O上的

Background Service Limitations

以及最新的Android P上面的

App Standby Buckets

。為了確定背景任務對電量的消耗影響足夠小,對待背景任務的處理要更加的慎重小心。

0)Types of Background Work

通常來說,我們可以把所有的背景任務按照任務緊迫性(是馬上需要執行的任務/還是可以緩期執行的任務)和任務重要性(是確定一定要被執行的任務/還是最好能夠執行的任務)進行四象限的劃分。通常來說對于非確定一定要執行的任務,無論時間是否緊迫,我們都可以使用ThreadPool來完成這個任務。對于那些比較重要的又時間緊迫的任務,我們一般會使用Foreground Service來完成這個操作。比較有有意思的是最後一個象限:那些希望確定可以被執行但是又可以接受延期執行的任務。這些任務可以使用JobScheduler/JobDispatcher/AlarmManager/BroadcastReceivers來完成。WorkManager也剛好是用來解決這一類的問題的。

1)WorkManager Features

下面是WorkManager的一些突出特點:

  • 確定可以被執行,并且可以設定執行的限定條件(例如僅僅在有網絡連接配接的時候才進行圖檔的上傳)
  • 同樣受到系統背景任務的限制管理(如APP進入Doze Mode的時候,任務不會被執行)
  • 向後相容;無論是否內建了Google Play Service服務,都是向後相容的
  • 任務可查詢;如論當下在執行什麼任務,都是可以直接查詢擷取到任務狀态資訊的(例如正在運作的狀态是什麼,結果是成功還是失敗了)
  • 任務可串聯;例如執行任務A之前需要任務B或者C先進行完成
  • 任務伺機執行:在條件滿足的時候會盡快嘗試觸發任務的執行,不需要等待JobScheduler的喚醒,也不會需要等待JobScheduler進行批量任務處理的才被執行

WorkManager中的核心類有:

  • Worker:這個類是真正幹活的,工作邏輯都在這裡面
  • WorkRequest:
    • OneTimeWorkRequest:隻執行一次的任務請求
    • PeriodicWorkRequest:重複執行的任務請求

舉個例子:圖檔上傳的背景任務是如何執行的。下面是上傳圖檔的Worker示例:

其中uploadPhoto是執行在背景線程的,傳回值可以是成功或者失敗,還可以是重試,這意味着告訴系統這個任務需要後面找機會重新執行。有了上面那些基礎,接下去就隻需要利用Worker建立對應的WorkRequest,并并添加到WorkManager的執行隊列中就好了。

正常情況下,放到任務隊列中的任務會被立馬執行,可是如果遇到網絡連接配接失敗的情況,這樣就會執行失敗。此時我們就可以通過添加限定執行條件來達到優化的目的,例如設定限定隻在網絡連接配接成功的時候才進行任務的執行。

2)Observing Work

有了上面的任務觸發邏輯之後,那麼如何做任務的監聽呢?例如正在處理過程中顯示一個進度圈,處理成功的時候消失進度等等。我們可以使用如下示範的範例來監聽任務的執行狀态。

LiveData

是Google開發的一個感覺生命周期的架構元件。使用這個元件來hook監聽request任務的

WorkStatus

。在WorkStatus裡面有任務的id和status,其中status有6種狀态,分别是

ENQUEUED

,

RUNNING

SUCCEEDED

FAILED

BLOCKED

CANCELLED

3)Chaining Work

通常來說,上傳任務真正被執行之前,我們會對資料做一次壓縮,因為每一個任務都需要在背景進行,并且需要保證執行順序。我們可以使用下面的示例方式,先進行壓縮,成功之後,再進行上傳。

隻是以可以類似上面那樣寫,是因為每一步任務傳回的都是

WorkContinuation

,使用它可以對不同的任務進行串聯。

如果想要多項任務并發執行,可以同時建立多個WorkRequest,一起交給WorkManager進行執行(根據CPU核心數和架構的不同,并發數量有所差異)。

我們再把任務鍊設定的更加複雜一點,例如圖檔要先分别經過不同的濾鏡處理,之後再進行壓縮,最後才可以上傳,那麼使用WorkManager該如何實行呢?

4)Inputs and Outputs

任務之間如何進行資料的傳遞呢?在介紹這個之前,我們需要了解下什麼叫做MapReduce。例如,我們想要從三本書裡面找出使用最多的詞語,先把所有詞語都進行計算一遍,然後對詞語的使用次數進行排序,最後才可以找出使用最多的詞語,我們把這個行為叫做MapReduce。

使用WorkManager的輸入和輸出資料具備如下的特點:

  • 簡單的KEY-VALUE
    • KEY都是String類型的
    • VALUE可以是基礎資料類型和String
  • 資料本身已經做了序列化處理
  • 限定10KB大小以内

我們使用如下的方式進行輸入的資料傳遞,構造一個map類型的Data,通過WorkManager的

setInputData()

給Worker進行傳輸資料。

接下去Worker可以通過

getInputData()

來擷取到輸入的資料。

一般來說,我們會需要把處理的結果進行傳回,那麼使用

setOutputData()

來完成這個操作就可以了

有意思的事情是,在任務鍊中,輸出的資料一般就是下一個任務的輸入。那麼當某個環節的一個任務是由多個任務的輸出構成的時候,改如何處理呢?

為了解決這個問題,我們需要了解

InputMergers

,顧名思義,它是用來合并多個輸入資料變成一個的。一般來說有兩種合并實作的方式(也可以自己自定義)

  • OverwritingInputMerger

    (系統預設):按照輸入資料的先後順序,相同KEY會被覆寫,不同的KEY内容會被保留
  • ArrayCreatingInputMerger

    :相同KEY的VALUE值進行合并,需要確定VALUE是相同資料類型的,否者會出現異常

5)Cancelling Work

想要取消一個任務,隻需要調用

cancelWorkById()

就好了,但是需要注意的是,這個方法隻是盡力而為,因為相關想要取消的任務有可能已經在運作,也有可能已經執行結束了。

6)Tags

前面我們有提到過好幾次任務id,這個id是系統自動生成的,類似UUID這樣的數值。我們無法通過這個id來判斷這是一個什麼樣的任務,tags就是為了解決這個任務可讀性的問題的。我們可以給任務打上一個或者多個tag來标記這是一個什麼樣的任務,然後可以通過這個tag來查詢,取消任務等等。

使用Tag可以給我們提供很大的幫助,我們可以根據不同的子產品和依賴給任務設定不同的tag,也可以根據任務的類型進行設定tag,這樣就可以友善的進行批量任務操作了。

7)Unique Work

為了解決多個任務的同步問題,引入了Unique Work的機制。它有三種類型,分别為

  • KEEP

    :新啟動的Unique任務,如果之前已經存在,就繼續保留舊的任務,如果不存在,則觸發這次新的任務
  • REPLACE

    :取消或者删除之前的所有此類Unique任務,使用這次的任務作為最新任務,重複調用多次的時候,會以最後一次為準
  • APPEND

    :按照添加順序,逐個執行任務

8)Periodic Work

重複任務和我們之前認知的其他重複任務一樣,具備一些如下的特點:

  • 最短間隔時間15分鐘(和JobScheduler一樣)
  • 同樣受系統doze mode和其他的背景任務限制
  • 不可以有任務鍊
  • 不可以有觸發延遲

9)Under The Hood

當系統接受到一個Work任務的時候,會先記錄到自己的任務資料庫中,接下去系統是如何判斷執行的呢?如果任務符合當下執行的條件,那麼會由Executor(可自定義,系統預設有實作)立即執行;如果我們的程序已經被殺死,那麼任務什麼時候可以被執行呢?如果裝置運作在>=API 23,會交給JobScheduler觸發IPC請求,喚醒我們的程序進行任務的執行;如果裝置運作在< API 23的情況下,系統會判斷裝置是否有Firebase JobDispatcher,如果有會交給它進行處理;如果那些Google Play Service服務都沒有,系統會使用AlarmManager和BroadcastReceivers的方式在合适的時候喚醒應用進行處理。

10)Best Practice

  • 何時使用WorkManager呢?下面給一些最佳實踐的例子:
    • OK:上傳圖檔和視訊
    • OK:解析資料并存儲到資料庫中
    • NO:從調色闆中擷取顔色并設定到圖檔上
    • NO:解析資料并呈現到視圖上進行顯示
    • NO:處理交易的請求
  • 不要使用WorkManager來存儲資料,記得隻有10kb的限制
  • 記得給不同的任務設定各自的執行限定條件,避免無謂的資源浪費

原文作者:胡凱

原文連結

http://hukai.me/google-io-2018-android-jetpack-workmanager/