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