摘要:讓我們探討一下如何確保你的工作脫離主線程運行并保證執行。這確保在默認情況下,你的工作是同步運行的,并且在主線程之外運行。這是應該脫離主線程運行的工作,但是,因為它與直接相關,所以如果關閉應用程序則不需要繼續。
原文地址:WorkManager Basics
原文作者:Lyla Fujiwara
譯文出自:掘金翻譯計劃
本文永久鏈接:github.com/xitu/gold-m…
譯者:Rickon
校對者:Feximin
插圖來自 Virginia Poltrack
歡迎來到我們 WorkManager 系列的第二篇文章。WorkManager 是一個 Android Jetpack 庫,當滿足工作的約束條件時,用來運行可延遲、需要保障的后臺工作。對于許多類型的后臺工作,WorkManager 是當前的最佳實踐方案。在第一篇博文中,我們討論了 WorkManager 是什么以及何時使用 WorkManager。
在這篇博文中,我將介紹:
將你的后臺任務定義為工作
定義特定的工作應該如何運行
運行你的工作
使用鏈進行存在依賴的工作
監視你的工作的狀態
我還將解釋 WorkManager 幕后發生的事情,以便你可以就如何使用它做出明智的決定。
從一個例子開始假設你有一個圖片編輯應用,可讓你給圖像加上濾鏡并將其上傳到網絡讓全世界看到。你希望創建一系列后臺任務,這些任務用于濾鏡,壓縮圖像和之后的上傳。在每個環節,都有一個需要檢查的約束——給圖像加濾鏡時要有足夠的電量,壓縮圖像時要有足夠的存儲空間,以及上傳圖像時要有網絡連接。
這個例子如上圖所示
這個例子正是具有以下特點的任務:
可延遲的,因為你不需要它立即執行,而且實際上可能希望等待某些約束被滿足(例如等待網絡連接)。
需要確保能夠運行,無論應用程序是否退出,因為如果加了濾鏡后的圖像永遠沒能與世界共享,你的用戶會非常不滿意!
這些特點使我們的圖像加濾鏡和上傳任務成為 WorkManager 的完美用例。
添加 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”。
定義你的 work 做什么在我們將多個任務連接在一起之前,讓我們關注如何執行一項工作。我將會著重細說上傳任務。首先,你需要創建自己的 Worker 實現類。我將會把我們的類命名為 UploadWorker,然后重寫 doWork() 方法。
Workers:
定義你的工作實際做了什么。
接受輸入并產生輸出。輸入和輸出都以鍵值對表示。
始終返回表示成功,失敗或重試的值。
這是一個示例,展示了如何實現上傳圖像的 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() 選項,它將在之后的時間再次重試你的工作。
定義您的 work 應該如何運行一方面 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() .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 時自定義退避條件。這允許你定義何時應重試運行。
退避條件由兩個屬性定義:
BackoffPolicy,默認為指數性的,但是可以設置為線性。
持續時間,默認為 30 秒。
用于對上傳工作進行排隊的組合代碼如下,包括約束,輸入和自定義退避策略:
// 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運行 work() .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 所示)。
保障 你的工作將會運行(即使你重啟設備或應用程序退出,它也不會忘記運行你的工作)。
根據用戶 API 級別的最佳實踐運行(如上一篇文章所述)。
讓我們探討一下 WorkManager 如何確保你的工作脫離主線程運行并保證執行。在幕后,WorkManager 包括以下部分:
內部 TaskExecutor:一個單線程 Executor,處理所有排隊工作的請求。如果您不熟悉 Executors,可以在這里閱讀更多相關信息。
WorkManager 數據庫:一個本地數據庫,可跟蹤所有工作的所有信息和狀態。這包括工作的當前狀態,工作的輸入和輸出以及對工作的任何約束限制。此數據庫使 WorkManager 能夠保證你的工作能夠完成 —— 如果你的用戶的設備重新啟動并且工作中斷,則可以從數據庫中提取工作的所有詳細信息,并在設備再次啟動時重新啟動工作。
WorkerFactory:一個默認工廠,用于創建 Worker 的實例。我們將在以后的博文中介紹為什么以及如何配置它。
Default Executor:一個默認的執行程序,運行你的工作,除非你另行指定。這確保在默認情況下,你的工作是同步運行的,并且在主線程之外運行。
這些部分可以被重寫以具有不同的行為。
來自:Working with WorkManager Android 開發者大會展示 2018
當你安排 WorkRequest:
內部 TaskExecutor 立即將你的 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() .setInputMerger(ArrayCreatingInputMerger::class.java) .setConstraints(constraints) .build()
請注意,InputMerger 是添加到 compressWorkRequest 中的,而不是并行的三個濾鏡請求中的。
假設每個濾鏡工作請求的輸出是映射到圖像 URI 的鍵 “KEY_IMAGE_URI”。添加 ArrayCreatingInputMerger 的作用是并行請求的輸出,當這些輸出具有匹配的鍵時,它會創建一個包含所有輸出值的數組,映射到單個鍵。可視化圖表如下:
ArrayCreatingInputMerger 功能可視化
因此,compressWorkRequest 的輸入將最終成為映射到濾鏡圖像 URI 數組的 “KEY_IMAGE_URI” 對。
觀察你的 WorkRequest 狀態監視工作的最簡單方法是使用 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 works 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
“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
WorkManager 相關資源官方文檔
參考指南
WorkManager 1.0.0-beta02 Release notes
Codelab:Kotlin 和 Java.
源碼(AOSP 的一部分)
Working with WorkManager (Android 開發者峰會 2018) presentation
Issue Tracker
StackOverflow 上面的 WorkManager 相關問題
Google’s Power blog post series
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改并 PR,也可獲得相應獎勵積分。文章開頭的 本文永久鏈接 即為本文在 GitHub 上的 MarkDown 鏈接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、后端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7044.html
摘要:前言上一次我們對的應用進行了一次全面的分析,這一次我們來聊聊。 showImg(https://segmentfault.com/img/remote/1460000020077803?w=1280&h=853); 前言 上一次我們對Paging的應用進行了一次全面的分析,這一次我們來聊聊WorkManager。 如果你對Paging還未了解,推薦閱讀這篇文章: Paging在Recy...
摘要:通過實現一次請求來解釋使用協程中的實際問題是這篇文章的重點。當接收一個新事件時,啟動一個新的協程來對列表進行排序,并在響應時更新。在中啟動協程作為一般模式。因此,在默認情況下,在存儲庫中啟動的任何協程都會泄露。 原文鏈接:Coroutines On Android (part III): Real work 原文作者:Sean McQuillan 這是一篇關于在 Android 上使用...
閱讀 713·2023-04-25 19:43
閱讀 3907·2021-11-30 14:52
閱讀 3784·2021-11-30 14:52
閱讀 3852·2021-11-29 11:00
閱讀 3783·2021-11-29 11:00
閱讀 3869·2021-11-29 11:00
閱讀 3557·2021-11-29 11:00
閱讀 6105·2021-11-29 11:00