自定义 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,感兴趣的读者请继续关注。

相关文章
相关标签/搜索