WorkManager 是一個 Android Jetpack 擴展庫,它可讓您輕鬆規劃那些可延後、異步但又須要可靠運行的任務。對於絕大部分後臺執行任務來講,使用 WorkManager 是目前 Android 平臺上的最佳實踐。java
目前爲止本系列已經討論過:node
在本篇文章中,咱們將會討論自定義配置相關的內容,包括:android
本系列的下一篇文章將對依賴注入和 Dagger 展開討論,請持續關注咱們。服務器
使用 WorkManager 時,您須要本身定義 Worker/CoroutineWorker 或任何 ListenableWorker 的派生類。WorkManager 會在正確的時間點實例化您的 Worker,其時機獨立於您應用的運行,不受其運行狀態的影響。爲了能夠初始化您的 Worker,WorkManager 會使用一個 WorkerFactory。app
默認 WorkerFactory 所建立的 Worker 只包含兩個參數:異步
若是您須要經過 Worker 的構造函數傳入更多參數,則須要一個自定義的 WorkerFactory。ide
延伸閱讀 : 咱們講過默認的 WorkerFactory 使用反射來實例化正確的 ListenableWorker 類,但當咱們的 Worker 類的類名被 R8 (或 ProGuard) 最小化以後,這個操做就會失敗。爲了不這種狀況,WorkManager 包含了一個 proguard-rules.pro 文件來避免您的 Worker 類的類名被混淆。
WorkManager 類遵循 單例模式,並且它只能在實例化以前進行配置。這意味着,若是您想自定義它的配置,就必須先禁用默認配置。函數
若是您嘗試經過 initialize() 方法再次初始化 WorkManager,該方法就會拋出一個異常 (於 1.0.0 版本中加入)。爲了不異常,您須要禁用默認的初始化。您能夠稍後在您的 Application 的 onCreate 方法中配置和初始化您的 WorkManager。ui
2.1.0 版本 中加入了一個更好的初始化 WorkManager 的方式。您能夠經過在您的 Application 類中實現 WorkManager 的 Configuration.Provider 接口的方式來使用按需初始化。接下來,您只須要使用 getInstance(context)) 得到實例,WorkManager 就會經過您的配置初始化它本身。google
如上所講,您能夠配置用來建立 Worker 的 WorkerFactory,可是您也能夠自定義其餘的參數。WorkManager 的 Configuration.Builder 參考指南中包含了參數的完整列表。這裏我想強調兩個附加參數:
當咱們有須要時,能夠經過修改日誌級別方便地理解 WorkManager 中正在發生什麼。關於這個話題,咱們有一個 專門的文檔頁。您也能夠查看 Advanced WorkManager codelab 實戰教程,以瞭解此功能在真實示例中的實現,以及您能夠經過此功能獲取到什麼樣的信息。
若是以在咱們的應用中使用 JobScheduler API 同樣的方式使用 WorkManager,咱們可能也會想要自定義 JobId 範圍。由於在這種狀況下,您會想要避免在同一個地方使用相同的 JobId 範圍。版本 2.4.0 中也加入了一個新的 Lint 規則 來覆蓋這種狀況。
咱們已經知道,WorkManager 有一個默認的 WorkerFactory,它能夠根據咱們經由 WorkRequest 傳入的 Worker 類的類名,經過反射來找到應該實例化的 Worker 類。
⚠️ 若是您在建立了一個 WorkRequest 後重構了應用,併爲您的 Worker 類起了另外一個名字,WorkManager 就會由於沒法找到正確的類而拋出一個 ClassNotFoundException。
您可能會想要爲您的 Worker 的構造函數添加其餘參數。假設您有一個 Worker 須要引用一個 Retrofit 服務來跟遠程服務器進行通信:
/* 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() } } } }
若是咱們在一個應用上做出上面的修改,程序仍然可被正常編譯。可是隻要代碼被執行、WorkManager 嘗試去實例化這個 CoroutineWorker 時,應用就會由於拋出異常而被關閉。異常的描述爲沒法找到正確的方法來進行實例化:
Caused by java.lang.NoSuchMethodException: <init> [class android.content.Context, class androidx.work.WorkerParameters]
這時咱們就須要一個自定義的 WorkerFactory。
可是彆着急,咱們已經看到其中涉及的幾個步驟。如今讓咱們回顧一下咱們已經作了的事情,而後深刻了解其中每一步的詳細信息:
如 WorkManager 的文檔 中描述,禁用操做要在您的 AndroidManifest.xml 文件中完成。移除默認狀況下從 WorkManager 庫中自動合併的節點。
<!-- 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>
爲了建立包含正確參數的 Worker,如今須要實現咱們本身的工廠 (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? { // 這裏只能處理一個 Worker,請不要這樣作! // 參考下文來更好地使用 DelegatingWorkerFactory return UpvoteStoryWorker(appContext, workerParameters, DesignerNewsService) } }
接下來,咱們必須將咱們的工廠註冊到咱們的 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() ... }
當您的應用中只有一個 Worker 類時,以上即是您所須要作的全部事情。而若是您有多個或者預計將來會有多個 Worker 類,更好的解決方案是使用在 2.1 版中加入的 DelegatingWorkerFactory。
咱們能夠經過使用 DelegatingWorkerFactory 來替代將 WorkManager 配置爲直接使用某個工廠的操做。咱們可使用 DelegatingWorkerFactory 的 addFactory()) 方法向其添加咱們的工廠,這樣一來,您就有了多個工廠,其中每一個均可以管理一個或多個 Worker。在 DelegatingWorkerFactory 中註冊您的工廠,這將有助於協調多個工廠的執行。
在這種狀況下,您的工廠須要檢查是否知道如何處理做爲參數傳入的 workerClassName。若是答案是否認的,就返回 null,而 DelegatingWorkerFactory 便會去尋找下一個註冊的工廠。若是沒有任何被註冊的工廠知道如何處理某個類,那麼它將回退到使用反射的默認工廠。
下面是咱們的工廠類代碼,修改成當它不知道如何處理某個 workerClassName 時,將返回 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,這樣基類就能夠代理到默認的 WorkerFactory null } } }
咱們的 WorkManager 配置會變成:
/* 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)) // 在這裏添加您應用中可能會使用的其餘 WorkerFactory return Configuration.Builder() .setMinimumLoggingLevel(android.util.Log.INFO) .setWorkerFactory(myWorkerFactory) .build() } ... }
若是您有多個 Worker 須要不一樣的參數,您能夠建立第二個 WorkerFactory,並經過再次調用 addFactory 來添加它。
WorkManager 是一個功能十分強大的庫,它的默認配置已經能夠覆蓋許多常見的使用場景。然而當您遇到某些狀況時,諸如須要增長日誌級別或須要傳入額外參數到您的 Worker 時,則須要一個自定義的配置。
但願您能經過本文對此主題有一個良好的認識。若是您有任何疑問,能夠在評論區中留言。
接下來的文章咱們將會討論如何在自定義 WorkManager 配置時使用 Dagger,感興趣的讀者請繼續關注。