自定義 WorkManager —— 基礎概念

WorkManager 是一個 Android Jetpack 擴展庫,它可讓您輕鬆規劃那些可延後、異步但又須要可靠運行的任務。對於絕大部分後臺執行任務來講,使用 WorkManager 是目前 Android 平臺上的最佳實踐。java

目前爲止本系列已經討論過:node

在本篇文章中,咱們將會討論自定義配置相關的內容,包括:android

  • 爲何可能會須要自定義配置
  • 如何聲明自定義配置
  • WorkerFactory 以及自定義 WorkerFactory 的緣由
  • DelegatingWorkerFactory 詳解

本系列的下一篇文章將對依賴注入和 Dagger 展開討論,請持續關注咱們。服務器

使用 WorkManager 時,您須要本身定義 Worker/CoroutineWorker 或任何 ListenableWorker 的派生類。WorkManager 會在正確的時間點實例化您的 Worker,其時機獨立於您應用的運行,不受其運行狀態的影響。爲了能夠初始化您的 Worker,WorkManager 會使用一個 WorkerFactoryapp

默認 WorkerFactory 所建立的 Worker 只包含兩個參數:異步

若是您須要經過 Worker 的構造函數傳入更多參數,則須要一個自定義的 WorkerFactory。ide

延伸閱讀 : 咱們講過默認的 WorkerFactory 使用反射來實例化正確的 ListenableWorker 類,但當咱們的 Worker 類的類名被 R8 (或 ProGuard) 最小化以後,這個操做就會失敗。爲了不這種狀況,WorkManager 包含了一個  proguard-rules.pro 文件來避免您的 Worker 類的類名被混淆。

自定義配置和 WorkerFactory

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 參考指南中包含了參數的完整列表。這裏我想強調兩個附加參數:

  • Logging 級別
  • JobId 範圍

當咱們有須要時,能夠經過修改日誌級別方便地理解 WorkManager 中正在發生什麼。關於這個話題,咱們有一個 專門的文檔頁。您也能夠查看 Advanced WorkManager codelab 實戰教程,以瞭解此功能在真實示例中的實現,以及您能夠經過此功能獲取到什麼樣的信息。

若是以在咱們的應用中使用 JobScheduler API 同樣的方式使用 WorkManager,咱們可能也會想要自定義 JobId 範圍。由於在這種狀況下,您會想要避免在同一個地方使用相同的 JobId 範圍。版本 2.4.0 中也加入了一個新的 Lint 規則 來覆蓋這種狀況。

WorkManager 的 WorkerFactory

咱們已經知道,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。

可是彆着急,咱們已經看到其中涉及的幾個步驟。如今讓咱們回顧一下咱們已經作了的事情,而後深刻了解其中每一步的詳細信息:

  1. 禁用默認初始化
  2. 實現一個自定義 WorkerFactory
  3. 建立自定義配置
  4. 初始化 WorkManager

禁用默認初始化

如 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>

實現一個自定義 WorkerFactory

爲了建立包含正確參數的 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)

    }
}

建立一個自定義 WorkerConfiguration**

接下來,咱們必須將咱們的工廠註冊到咱們的 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

當您的應用中只有一個 Worker 類時,以上即是您所須要作的全部事情。而若是您有多個或者預計將來會有多個 Worker 類,更好的解決方案是使用在 2.1 版中加入的 DelegatingWorkerFactory

使用 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,感興趣的讀者請繼續關注。

相關文章
相關標籤/搜索