- 原文地址:WorkManager Basics
- 原文做者:Lyla Fujiwara
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:Rickon
- 校對者:Feximin
插圖來自 Virginia Poltrackhtml
歡迎來到咱們 WorkManager 系列的第二篇文章。WorkManager 是一個 Android Jetpack 庫,當知足工做的約束條件時,用來運行可延遲、須要保障的後臺工做。對於許多類型的後臺工做,WorkManager 是當前的最佳實踐方案。在第一篇博文中,咱們討論了 WorkManager 是什麼以及什麼時候使用 WorkManager。前端
在這篇博文中,我將介紹:java
我還將解釋 WorkManager 幕後發生的事情,以便你能夠就如何使用它作出明智的決定。android
假設你有一個圖片編輯應用,可以讓你給圖像加上濾鏡並將其上傳到網絡讓全世界看到。你但願建立一系列後臺任務,這些任務用於濾鏡,壓縮圖像和以後的上傳。在每一個環節,都有一個須要檢查的約束——給圖像加濾鏡時要有足夠的電量,壓縮圖像時要有足夠的存儲空間,以及上傳圖像時要有網絡鏈接。ios
這個例子如上圖所示git
這個例子正是具備如下特色的任務:github
這些特色使咱們的圖像加濾鏡和上傳任務成爲 WorkManager 的完美用例。數據庫
本文使用 Kotlin 書寫代碼,使用 KTX 庫(KoTlin eXtensions)。KTX 版本的庫提供了 擴展函數 爲了更簡潔和習慣的使用 Kotlin。你能夠添加以下依賴來使用 KTX 版本的 WorkManager:後端
dependencies {
def work_version = "1.0.0-beta02"
implementation "android.arch.work:work-runtime-ktx:$work_version"
}
複製代碼
你能夠在 這裏](developer.android.com/topic/libra…) 到該庫的最新版本。若是你想使用 Java 依賴,那就移除「-ktx」。數組
在咱們將多個任務鏈接在一塊兒以前,讓咱們關注如何執行一項工做。我將會着重細說上傳任務。首先,你須要建立本身的 Worker
實現類。我將會把咱們的類命名爲 UploadWorker
,而後重寫 doWork()
方法。
Worker
s:
這是一個示例,展現瞭如何實現上傳圖像的 Worker
:
class UploadWorker(appContext: Context, workerParams: WorkerParameters)
: Worker(appContext, workerParams) {
override fun doWork(): Result {
try {
// Get the input
val imageUriInput = inputData.getString(Constants.KEY_IMAGE_URI)
// Do the work
val response = upload(imageUriInput)
// Create the output of the work
val imageResponse = response.body()
val imgLink = imageResponse.data.link
// workDataOf (part of KTX) converts a list of pairs to a [Data] object.
val outputData = workDataOf(Constants.KEY_IMAGE_URI to imgLink)
return Result.success(outputData)
} catch (e: Exception) {
return Result.failure()
}
}
fun upload(imageUri: String): Response {
TODO(「Webservice request code here」)
// Webservice request code here; note this would need to be run
// synchronously for reasons explained below.
}
}
複製代碼
有兩點須要注意:
Data
傳遞,它本質上是原始類型和數組的映射。Data
對象應該至關小 —— 實際上能夠輸入/輸出的總大小有限制。這由 MAX_DATA_BYTES
設置。若是您須要將更多數據傳入和傳出 Worker
,則應將數據放在其餘地方,例如 Room database。做爲一個例子,我傳入上面圖像的 URI,而不是圖像自己。Result.success()
和 Result.failure()
。還有一個 Result.retry()
選項,它將在以後的時間再次重試你的工做。一方面 Worker
定義工做的做用,另外一方面 WorkRequest
定義應該如何以及什麼時候運行工做。
如下是爲 UploadWorker
建立 OneTimeWorkRequest
的示例。也能夠有重複性的 PeriodicWorkRequest
:
// workDataOf (part of KTX) converts a list of pairs to a [Data] object.
val imageData = workDataOf(Constants.KEY_IMAGE_URI to imageUriString)
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(imageData)
.build()
複製代碼
此 WorkRequest
將輸入 imageData: Data
對象,並儘快運行。
假設 UploadWork
並不老是應該當即運行 —— 它應該只在設備有網絡鏈接時運行。你能夠經過添加 Constraints
對象來完成此操做。你能夠建立這樣的約束:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
複製代碼
如下是其餘受支持約束的示例:
val constraints = Constraints.Builder()
.setRequiresBatteryNotLow(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.setRequiresStorageNotLow(true)
.setRequiresDeviceIdle(true)
.build()
複製代碼
最後,還記得 Result.retry()
嗎?我以前說過,若是 Worker
返回 Result.retry()
,WorkManager 將從新計劃工做。你能夠在建立新的 WorkRequest
時自定義退避條件。這容許你定義什麼時候應重試運行。
退避條件由兩個屬性定義:
用於對上傳工做進行排隊的組合代碼以下,包括約束,輸入和自定義退避策略:
// Create the Constraints
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
// Define the input
val imageData = workDataOf(Constants.KEY_IMAGE_URI to imageUriString)
// Bring it all together by creating the WorkRequest; this also sets the back off criteria
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(imageData)
.setConstraints(constraints)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
複製代碼
這些都很好,但你尚未真正調度好你的工做去運行。如下是告訴 WorkManager 調度工做所需的一行代碼:
WorkManager.getInstance().enqueue(uploadWorkRequest)
複製代碼
你首先須要獲取 WorkManager
的實例,這是一個負責執行你的工做的單例。調用 enqueue
來啓動 WorkManager
跟蹤和調度工做的整個過程。
那麼,WorkManager
能爲您作些什麼呢?默認狀況下,WorkManager
會:
Worker
類,如上面的 UploadWorker
所示)。讓咱們探討一下 WorkManager 如何確保你的工做脫離主線程運行並保證執行。在幕後,WorkManager 包括如下部分:
內部 TaskExecutor:一個單線程 Executor
,處理全部排隊工做的請求。若是您不熟悉 Executors
,能夠在這裏閱讀更多相關信息。
WorkManager 數據庫:一個本地數據庫,可跟蹤全部工做的全部信息和狀態。這包括工做的當前狀態,工做的輸入和輸出以及對工做的任何約束限制。此數據庫使 WorkManager 可以保證你的工做可以完成 —— 若是你的用戶的設備從新啓動而且工做中斷,則能夠從數據庫中提取工做的全部詳細信息,並在設備再次啓動時從新啓動工做。
WorkerFactory:一個默認工廠,用於建立 Worker
的實例。咱們將在之後的博文中介紹爲何以及如何配置它。
Default Executor:一個默認的執行程序,運行你的工做,除非你另行指定。這確保在默認狀況下,你的工做是同步運行的,而且在主線程以外運行。
這些部分能夠被重寫以具備不一樣的行爲。
來自:Working with WorkManager Android 開發者大會展現 2018
當你安排 WorkRequest
:
WorkRequest
信息保存到 WorkManager 數據庫。WorkRequest
的 Constraints
時(能夠當即發生),Internal TaskExecutor 會告訴 WorkerFactory
建立一個 Worker
。Executor
調用你的 Worker
的 doWork()
方法脫離主線程。經過這種方式,默認狀況下,你的工做均可以保證執行脫離主線程運行。
如今,若是你想使用除默認 Executor
以外的一些其餘機制來運行你的工做,也是能夠的!對協程(CoroutineWorker
)和 RxJava(RxWorker
)的開箱即用支持做爲工做的手段。
或者,你可使用 ListenableWorker
準確指定工做的執行方式。Worker
其實是 ListenableWorker
的一個實現,它默認在默認的 Executor
上運行你的工做,所以是同步的。因此,若是你想要徹底控制工做的線程策略或異步運行工做,你能夠將 ListenableWorker
子類化(具體細節將在後面的文章中討論)。
WorkManager 雖然將全部工做信息保存到數據庫中有些麻煩,但它仍是會作,這使得它成了很是適合須要保障執行的任務。這也是使得 WorkManager 輕鬆應對對於不須要保障且只須要在後臺線程上執行的任務的的緣由。例如,假設你已經下載了圖像,而且但願根據該圖像更改 UI 部分的顏色。這是應該脫離主線程運行的工做,可是,由於它與 UI 直接相關,因此若是關閉應用程序則不須要繼續。因此在這樣的狀況下,不要使用 WorkManager —— 堅持使用像 Kotlin 協程那樣輕量的東西或建立本身的 Executor
。
咱們的濾鏡示例包含的不只僅是一個任務 —— 咱們想要給多個圖像加濾鏡,而後壓縮並上傳。若是要一個接一個地或並行地運行一系列 WorkRequests
,則可使用 鏈。示例圖顯示了一個鏈,其中有三個並行運行的濾鏡任務,後面是壓縮任務和上傳任務,按順序運行:
使用 WorkManager 很是簡單。假設你已經用適當的約束建立了全部 WorkRequests,代碼以下所示:
WorkManager.getInstance()
.beginWith(Arrays.asList(
filterImageOneWorkRequest,
filterImageTwoWorkRequest,
filterImageThreeWorkRequest))
.then(compressWorkRequest)
.then(uploadWorkRequest)
.enqueue()
複製代碼
三個圖片濾鏡 WorkRequests
並行執行。一旦完成全部濾鏡 WorkRequests (而且只有完成全部三個),就會發生 compressWorkRequest
,而後是 uploadWorkRequest
。
鏈的另外一個優勢是:一個 WorkRequest
的輸出做爲下一個 WorkRequest
的輸入。所以,假設你正確設置了輸入和輸出數據,就像我上面的 UploadWorker
示例所作的那樣,這些值將自動傳遞。
爲了處理並行的三個濾鏡工做請求的輸出,可使用 InputMerger
,特別是 ArrayCreatingInputMerger
。代碼以下:
val compressWorkRequest = OneTimeWorkRequestBuilder<CompressWorker>()
.setInputMerger(ArrayCreatingInputMerger::class.java)
.setConstraints(constraints)
.build()
複製代碼
請注意,InputMerger
是添加到 compressWorkRequest
中的,而不是並行的三個濾鏡請求中的。
假設每一個濾鏡工做請求的輸出是映射到圖像 URI 的鍵 「KEY_IMAGE_URI」。添加 ArrayCreatingInputMerger
的做用是並行請求的輸出,當這些輸出具備匹配的鍵時,它會建立一個包含全部輸出值的數組,映射到單個鍵。可視化圖表以下:
ArrayCreatingInputMerger
功能可視化
所以,compressWorkRequest
的輸入將最終成爲映射到濾鏡圖像 URI 數組的 「KEY_IMAGE_URI」 對。
監視工做的最簡單方法是使用 LiveData
類。若是你不熟悉 LiveData,它是一個生命週期感知的可監視數據持有者 —— 這裏 對此有更詳細的描述。
調用 getWorkInfoByIdLiveData
返回一個 WorkInfo
的 LiveData
。WorkInfo
包含輸出的數據和表示工做狀態的枚舉。當工做順利完成後,它的 State
就會是 SUCCEEDED
。所以,例如,你能夠經過編寫一些監視代碼來實現當工做完成時自動顯示該圖像:
// In your UI (activity, fragment, etc)
WorkManager.getInstance().getWorkInfoByIdLiveData(uploadWorkRequest.id)
.observe(lifecycleOwner, Observer { workInfo ->
// Check if the current work's state is "successfully finished" if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) { displayImage(workInfo.outputData.getString(KEY_IMAGE_URI)) } }) 複製代碼
有幾點須要注意:
WorkRequest
都有一個惟一的 id,該惟一 id 是查找關聯 WorkInfo
的一種方法。WorkInfo
更改時進行監視並被通知的能力是 LiveData
提供的功能。工做有一個由不一樣 State
表明的生命週期。監視 LiveData<WorkInfo>
時,你會看到這些狀態;例如,你可能會看到:
「happy path」 或工做狀態
工做狀態經歷的 「happy path」 以下:
BLOCKED
:只有當工做在鏈中而且不是鏈中的下一個工做時纔會出現這種狀態。ENQUEUED
:只要工做是工做鏈中的下一個而且有資格運行,工做就會進入這個狀態。這項工做可能仍在等待 Constraint
被知足。RUNNING
:在這種狀態時,工做正在運行。對於 Worker
,這意味着 doWork()
方法已經被調用。SUCCEEDED
:當 doWork()
返回 Result.success()
時,工做進入這種最終狀態。如今,當工做處於 RUNNING
狀態,你能夠調用 Result.retry()
。這將會致使工做退回 ENQUEUED
狀態。工做也可能隨時被 CANCELLED
。
若是工做運行的結果是 Result.failure()
而不是成功。它的狀態將會以 FAILED
結束,所以,狀態的完整流程圖以下所示:
(來自:Working with WorkManager Android 開發者峯會 2018)
想看精彩的視頻講解,請查看 WorkManager Android 開發者峯會演講。
這就是 WorkManager API 的基礎知識。使用咱們剛剛介紹的代碼片斷,你如今就能夠:
Worker
。WorkRequest
、Constraint
、啓動輸入和退出策略配置 Worker
的運行方式。WorkRequest
。WorkManager
在線程和保障運行方面的幕後工做。WorkInfo
監視你的 WorkRequest
的狀態。想親自試試 WorkManager 嗎?查看 codelab,包含 Kotlin 和 Java 代碼。
隨着咱們繼續更新本系列,請繼續關注有關 WorkManager 主題的更多博客文章。 有什麼問題或者你但願咱們寫到的東西嗎?請在評論區告訴咱們!
感謝 Pietro Maggi
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。