WorkManager 是一個(gè) Android Jetpack 擴(kuò)展庫,它可以讓您輕松規(guī)劃那些可延后探膊、異步但又需要可靠運(yùn)行的任務(wù)页畦。對(duì)于絕大部分后臺(tái)執(zhí)行任務(wù)來說寄狼,使用 WorkManager 是目前 Android 平臺(tái)上的最佳實(shí)踐。
目前為止本系列已經(jīng)討論過:
- Android Jetpack WorkManager | Android 中文教學(xué)視頻
- WorkManager 在 Kotlin 中的實(shí)踐
- WorkManager: 周期性任務(wù)
在本篇文章中,我們將會(huì)討論自定義配置相關(guān)的內(nèi)容持寄,包括:
- 為什么可能會(huì)需要自定義配置
- 如何聲明自定義配置
- WorkerFactory 以及自定義 WorkerFactory 的原因
- DelegatingWorkerFactory 詳解
本系列的下一篇文章將對(duì)依賴注入和 Dagger 展開討論源梭,請(qǐng)持續(xù)關(guān)注我們。
使用 WorkManager 時(shí)稍味,您需要自己定義 Worker/CoroutineWorker 或任何 ListenableWorker 的派生類废麻。WorkManager 會(huì)在正確的時(shí)間點(diǎn)實(shí)例化您的 Worker,其時(shí)機(jī)獨(dú)立于您應(yīng)用的運(yùn)行模庐,不受其運(yùn)行狀態(tài)的影響烛愧。為了可以初始化您的 Worker,WorkManager 會(huì)使用一個(gè) WorkerFactory掂碱。
默認(rèn) WorkerFactory 所創(chuàng)建的 Worker 只包含兩個(gè)參數(shù):
- Application 的 Context
- WorkerParameters
如果您需要通過 Worker 的構(gòu)造函數(shù)傳入更多參數(shù)怜姿,則需要一個(gè)自定義的 WorkerFactory。
延伸閱讀 : 我們講過默認(rèn)的 WorkerFactory 使用反射來實(shí)例化正確的 ListenableWorker 類疼燥,但當(dāng)我們的 Worker 類的類名被 R8 (或 ProGuard) 最小化之后沧卢,這個(gè)操作就會(huì)失敗。為了避免這種情況醉者,WorkManager 包含了一個(gè) proguard-rules.pro 文件來避免您的 Worker 類的類名被混淆但狭。
自定義配置和 WorkerFactory
WorkManager 類遵循 單例模式,而且它只能在實(shí)例化之前進(jìn)行配置撬即。這意味著立磁,如果您想自定義它的配置,就必須先禁用默認(rèn)配置剥槐。
如果您嘗試通過 initialize() 方法再次初始化 WorkManager唱歧,該方法就會(huì)拋出一個(gè)異常 (于 1.0.0 版本中加入)。為了避免異常才沧,您需要禁用默認(rèn)的初始化迈喉。您可以稍后在您的 Application 的 onCreate 方法中配置和初始化您的 WorkManager。
2.1.0 版本 中加入了一個(gè)更好的初始化 WorkManager 的方式温圆。您可以通過在您的 Application 類中實(shí)現(xiàn) WorkManager 的 Configuration.Provider 接口的方式來使用按需初始化挨摸。接下來,您只需要使用 getInstance(context) 獲得實(shí)例岁歉,WorkManager 就會(huì)通過您的配置初始化它自己得运。
可配置參數(shù)
如上所講,您可以配置用來創(chuàng)建 Worker 的 WorkerFactory锅移,但是您也可以自定義其他的參數(shù)熔掺。WorkManager 的 Configuration.Builder 參考指南中包含了參數(shù)的完整列表。這里我想強(qiáng)調(diào)兩個(gè)附加參數(shù):
- Logging 級(jí)別
- JobId 范圍
當(dāng)我們有需要時(shí)非剃,可以通過修改日志級(jí)別方便地理解 WorkManager 中正在發(fā)生什么置逻。關(guān)于這個(gè)話題,我們有一個(gè) 專門的文檔頁备绽。您也可以查看 Advanced WorkManager codelab 實(shí)戰(zhàn)教程券坞,以了解此功能在真實(shí)示例中的實(shí)現(xiàn)鬓催,以及您可以通過此功能獲取到什么樣的信息。
如果以在我們的應(yīng)用中使用 JobScheduler API 一樣的方式使用 WorkManager恨锚,我們可能也會(huì)想要自定義 JobId 范圍宇驾。因?yàn)樵谶@種情況下,您會(huì)想要避免在同一個(gè)地方使用相同的 JobId 范圍猴伶。版本 2.4.0 中也加入了一個(gè)新的 Lint 規(guī)則 來覆蓋這種情況课舍。
WorkManager 的 WorkerFactory
我們已經(jīng)知道,WorkManager 有一個(gè)默認(rèn)的 WorkerFactory他挎,它可以根據(jù)我們經(jīng)由 WorkRequest 傳入的 Worker 類的類名筝尾,通過反射來找到應(yīng)該實(shí)例化的 Worker 類。
?? 如果您在創(chuàng)建了一個(gè) WorkRequest 后重構(gòu)了應(yīng)用雇盖,并為您的 Worker 類起了另一個(gè)名字忿等,WorkManager 就會(huì)因?yàn)闊o法找到正確的類而拋出一個(gè) ClassNotFoundException。
您可能會(huì)想要為您的 Worker 的構(gòu)造函數(shù)添加其他參數(shù)崔挖。假設(shè)您有一個(gè) Worker 需要引用一個(gè) Retrofit 服務(wù)來跟遠(yuǎn)程服務(wù)器進(jìn)行通訊:
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
class UpvoteStoryWorker(
appContext: Context,
workerParams: WorkerParameters,
private val service: UpvoteStoryHttpApi)
: CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
return try {
// 投票操作
Result.success()
} catch (e: Exception) {
if (runAttemptCount < MAX_NUMBER_OF_RETRY) {
Result.retry()
} else {
Result.failure()
}
}
}
}
如果我們?cè)谝粋€(gè)應(yīng)用上作出上面的修改,程序仍然可被正常編譯庵寞。但是只要代碼被執(zhí)行狸相、WorkManager 嘗試去實(shí)例化這個(gè) CoroutineWorker 時(shí),應(yīng)用就會(huì)因?yàn)閽伋霎惓6魂P(guān)閉捐川。異常的描述為無法找到正確的方法來進(jìn)行實(shí)例化:
Caused by java.lang.NoSuchMethodException: <init> [class android.content.Context, class androidx.work.WorkerParameters]
這時(shí)我們就需要一個(gè)自定義的 WorkerFactory脓鹃。
但是別著急,我們已經(jīng)看到其中涉及的幾個(gè)步驟」帕ぃ現(xiàn)在讓我們回顧一下我們已經(jīng)做了的事情瘸右,然后深入了解其中每一步的詳細(xì)信息:
- 禁用默認(rèn)初始化
- 實(shí)現(xiàn)一個(gè)自定義 WorkerFactory
- 創(chuàng)建自定義配置
- 初始化 WorkManager
禁用默認(rèn)初始化
如 WorkManager 的文檔 中描述,禁用操作要在您的 AndroidManifest.xml 文件中完成岩齿。移除默認(rèn)情況下從 WorkManager 庫中自動(dòng)合并的節(jié)點(diǎn)太颤。
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<application
…
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
實(shí)現(xiàn)一個(gè)自定義 WorkerFactory
為了創(chuàng)建包含正確參數(shù)的 Worker,現(xiàn)在需要實(shí)現(xiàn)我們自己的工廠 (factory):
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
class MyWorkerFactory(private val service: UpvoteStoryHttpApi) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
// 這里只能處理一個(gè) Worker盹沈,請(qǐng)不要這樣做龄章!
// 參考下文來更好地使用 DelegatingWorkerFactory
return UpvoteStoryWorker(appContext, workerParameters, DesignerNewsService)
}
}
創(chuàng)建一個(gè)自定義 WorkerConfiguration**
接下來,我們必須將我們的工廠注冊(cè)到我們的 WorkManager 的自定義配置中:
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.setWorkerFactory(MyWorkerFactory(DesignerNewsService))
.build()
...
}
初始化 WorkManager
當(dāng)您的應(yīng)用中只有一個(gè) Worker 類時(shí)乞封,以上便是您所需要做的所有事情做裙。而如果您有多個(gè)或者預(yù)計(jì)未來會(huì)有多個(gè) Worker 類,更好的解決方案是使用在 2.1 版中加入的 DelegatingWorkerFactory肃晚。
使用 DelegatingWorkerFactory
我們可以通過使用 DelegatingWorkerFactory 來替代將 WorkManager 配置為直接使用某個(gè)工廠的操作锚贱。我們可以使用 DelegatingWorkerFactory 的 addFactory() 方法向其添加我們的工廠,這樣一來关串,您就有了多個(gè)工廠拧廊,其中每個(gè)都可以管理一個(gè)或多個(gè) Worker监徘。在 DelegatingWorkerFactory 中注冊(cè)您的工廠,這將有助于協(xié)調(diào)多個(gè)工廠的執(zhí)行卦绣。
在這種情況下耐量,您的工廠需要檢查是否知道如何處理作為參數(shù)傳入的 workerClassName。如果答案是否定的滤港,就返回 null廊蜒,而 DelegatingWorkerFactory 便會(huì)去尋找下一個(gè)注冊(cè)的工廠。如果沒有任何被注冊(cè)的工廠知道如何處理某個(gè)類溅漾,那么它將回退到使用反射的默認(rèn)工廠山叮。
下面是我們的工廠類代碼,修改為當(dāng)它不知道如何處理某個(gè) workerClassName 時(shí)添履,將返回 null:
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
class MyWorkerFactory(private val service: DesignerNewsService) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return when(workerClassName) {
UpvoteStoryWorker::class.java.name ->
ConferenceDataWorker(appContext, workerParameters, service)
else ->
// 返回 null屁倔,這樣基類就可以代理到默認(rèn)的 WorkerFactory
null
}
}
}
我們的 WorkManager 配置會(huì)變成:
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration {
val myWorkerFactory = DelegatingWorkingFactory()
myWorkerFactory.addFactory(MyWorkerFactory(service))
// 在這里添加您應(yīng)用中可能會(huì)使用的其他 WorkerFactory
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.setWorkerFactory(myWorkerFactory)
.build()
}
...
}
如果您有多個(gè) Worker 需要不同的參數(shù),您可以創(chuàng)建第二個(gè) WorkerFactory暮胧,并通過再次調(diào)用 addFactory 來添加它锐借。
總結(jié)
WorkManager 是一個(gè)功能十分強(qiáng)大的庫,它的默認(rèn)配置已經(jīng)可以覆蓋許多常見的使用場景往衷。然而當(dāng)您遇到某些情況時(shí)钞翔,諸如需要增加日志級(jí)別或需要傳入額外參數(shù)到您的 Worker 時(shí),則需要一個(gè)自定義的配置席舍。
希望您能通過本文對(duì)此主題有一個(gè)良好的認(rèn)識(shí)布轿。如果您有任何疑問,可以在評(píng)論區(qū)中留言来颤。
接下來的文章我們將會(huì)討論如何在自定義 WorkManager 配置時(shí)使用 Dagger汰扭,感興趣的讀者請(qǐng)繼續(xù)關(guān)注。