文章目录
前言
前面已经学习了WorkManager的基本使用Jetpack之WorkManager(一),接下来我们学习如何自定义Manager。
一、自定义WorkManager
在本小节,我们将要涉及如下内容:
- 为什么需要自定义配置
- 如何自定义配置
- 什么是WorkerFactory并且为什么要自定义
- DelegatingWorkerFactory是什么
1.1 开始
当使用WorkManager时,你需要定义一个Worker(无论是Worker、CoroutineWorker还是ListenableWorker)的子类。在某个时刻,WorkManager会通过WorkerFactory实例化你定义的 Worker。
默认的WorkerFactory创建的Worker需要两个参数:
- Application’s Context
- WorkerParameters
如果你想要增加参数,那么只能通过自定义一个WorkerFactory实现。
默认的WorkerFactory使用反射技术实例化ListenableWorker。因此我们的类名不能混淆。
1.2 定义Configuration 和 WorkerFactory
WorkManager使用的是单例模式,因此之前在其初始化之前,改变其默认配置。在v1.0.0版本之后,如果初始化之后,再次调用初始化方法initialize(),app将会抛出响应的异常。但是到了v2.1.0版本之后,我们可以使用Configuration.Provider接口延迟初始化策略,这样我们就可以使用getInstance(context)获取相应实例。并且WorkManager将会使用你自定义的配置进行初始化。
1.2.1 可配置的参数
正如我上面所说,我们可以自定义其他参数来实例化一个Worker
- Logging level
- JobId range
我们定义上面两个参数的一样在于:Logging level 打印日志的级别。这对于我们查看、调试WorkManager是非常有帮助的;JobId range 是避免 当我们的app中有JobScheduer时,出现jobId冲突的问题。 针对这种case,在v2.4.0版本提供了一个检查类。
1.3 WorkManager的WorkerFactory
我们已经知道WorkManager有一个默认的WorkerFactory。它通过反射将我们在WorkRequest传入的worker类名 进行了实例化。
如果你创建一个WorkRequest ,然后使用其他的类名重构了你的app。WorkManager就找不到正确的类,从而抛出异常了。
你也许想要添加其他参数到Worker的构造方法中。如果你有一个Worker,想让他通过一个Retrofit的引用实现网络请求,那么我们应该像下面这样声明一个Worker:
class UpvoteStoryWorker(
appContext: Context,
workerParams: WorkerParameters,
private val service: UpvoteStoryHttpApi)
: CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
return try {
// Upvote story
Result.success()
} catch (e: Exception) {
if (runAttemptCount < MAX_NUMBER_OF_RETRY) {
Result.retry()
} else {
Result.failure()
}
}
}
}
接下来,我们使用该Worker。运行后,我们会发下app报如下异常:
Caused by java.lang.NoSuchMethodException: <init> [class android.content.Context, class androidx.work.WorkerParameters]
因为我们自定义的Worker参数增加了, 默认的WorkerFactory无法实例化它。因此我们需要自定义一个WorkerFactory。我们需要做如下事情:
- 关闭默认初始化
- 自定义一个WorkerFactory
- 自定义一个Configuration
- 初始化WorkerManager
1.3.1 关闭默认初始化
在AndroidManifest.xml中逸出默认初始化器。该结点是在WorkManager中默认声明的。
<application
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
1.3.2 实现自定义WorkerFactory
我们需要自定义一个WorkerFactory来实例化我们自定义的Worker
class MyWorkerFactory(private val service: UpvoteStoryHttpApi) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
// This only handles a single Worker, please don’t do this!!
// See below for a better way using DelegatingWorkerFactory
return UpvoteStoryWorker(appContext, workerParameters, DesignerNewsService)
}
}
1.3.3 创建WorkerConfiguration
接下来,我们需要将上面自定义的WorkerFactory注册到WorkerConfiguration中
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.setWorkerFactory(MyWorkerFactory(DesignerNewsService))
.build()
...
}
1.3.4 初始化WorkManager
如果你想要在应用中有多个Worker,请参考下一节实现。
1.4 DelegatingWorkerFactory
我们可以使用DelegatingWorkerFactory,将其设置到我们的WorkerFactory中,从而支持多个多个工厂。在这种情况下,如果一个WorkerFactory没有创建Worker,DelegatingFactory会去找到下一个WorkerFactory…
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 ->
// Return null, so that the base class can delegate to the default WorkerFactory.
null
}
}
}
我们的configuration将会这样使用:
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration {
val myWorkerFactory = DelegatingWorkingFactory()
myWorkerFactory.addFactory(MyWorkerFactory(service))
// Add here other factories that you may need in your application
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.setWorkerFactory(myWorkerFactory)
.build()
}
...
}
二、WorkManager和Dagger
2.1 为什么要使用Dagger
Dagger是google提供给Android开发者的一个依赖注入库。之前,我们已经了解如何自定义WorkerManager来传递多个参数。现在我们使用Dagger来注入这些参数。
2.2 使用Dagger注入参数到WorkerFactory
如果你使用Dagger管理你的依赖了,你需要把它集成到WorkerFactory上。如果你使用Dagger传递一个retrofit引用到你的Worker,那么Dagger需要将引用注入到WorkerFactory中来创建你的Worker。
现在我们假设Dagger提供了一个Retrofit Service实例的注入。这里不需要改变自定义工厂和WorkerManager的配置。仅仅是有一个被注入的参数:
@Singleton
class MyWorkerFactory @Inject constructor(
service: DesignerNewsService
) : DelegatingWorkerFactory() {
init {
addFactory(myWorkerFactory(service))
// Add here other factories that you may need in your application
}
}
在这个例子中,当我们初始化AppComponen后,我们使用AppComponent设置Dagger,开始先从Dagger到成员变量的赋值。
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): AppComponent
}
fun inject(application: MyApplication)
}
接下来初始化AppComponent:
class MyApplication : Application(), Configuration.Provider {
// other @Inject variables
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.factory().create(applicationContext)
appComponent.inject(this)
}
...
}
由于Dagger知道如何提供MyWorkerFactory的实例,你只需要使用@Inject获取实例,并使用它即可。
class MyApplication : Application(), Configuration.Provider {
@Inject lateinit var myWorkerFactory: MyWorkerFactory
...
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.setWorkerFactory(myWorkerFactory)
.build()
...
}