当客户端调用 WorkManager.initialize() 时,WorkManager 在库中不起作用
WorkManager doesn't work in library when client calls WorkManager.initialize()
我正在编写一个使用 WorkManager 的 Android 库。在代码的某处,我这样调用:
val uploadTripRequest = OneTimeWorkRequest.Builder(UploadTripWorker::class.java)
.setConstraints(someConstraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(WORKER_NAME, ExistingWorkPolicy.REPLACE, uploadTripRequest)
在我的 build.gradle 我有:implementation 'androidx.work:work-runtime:2.2.0'
通常情况下,一切都很完美。客户端应用程序调用 https://developer.android.com/reference/kotlin/androidx/work/WorkManager.html#initialize 时出现问题,例如:
WorkManager.initialize(context, Configuration.Builder().setWorkerFactory(myWorkerFactory).build())
调用它时,我的库中的 WorkManager 没有按预期工作。 doWork
方法没有在我的 UploadTripWorker
上调用,但它在客户 myWorkerFactory
提供的 worker 上执行。
从我的角度来看,WorkManager
在库中是不可用的,因为它是一个单例并且有人可以通过 initialize
方法改变它的行为。但我希望我是错的。
有什么办法可以解决这个问题吗?当然,我不能对我所有的客户说不要使用 WorkManager.initialize()
方法。
WorkManager v2.1 引入了可以在这些情况下提供帮助的 DelegatingWorkerFactory
class。
仍然需要应用程序开发人员以正确的方式实现它,正如您所说的 WorkManager 是单例的。
关键是,如果应用程序需要自定义初始化和自定义 WorkerFactory,它应该使用 DelegatingWorkerFactory
然后 add 它自己的 WorkerFactory。
这里的关键点是应用程序的 WorkerFactory 应该检查 worker class 名称以确保它是正确的(通常在构造函数中注入一些参数)。如果 class 名称不是应用程序所期望的,它可以 return null 并且 DelegatingWorkerFactory
将注意找到正确的工人 class 来实例化。
类似于(在应用程序代码中):
class MyWorkerFactory(
private val myInjectedParam: InjectedParam
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return when (workerClassName) {
MyWorker::class.java.name ->
MyWorker(appContext, workerParameters, myInjectedParam)
else ->
// Return null, so that the base class can delegate to the default WorkerFactory.
null
}
}
}
然后,应用需要将此 WorkerFactory 添加到已设置为自定义 WorkerFactory 的 DelegatingWorkerFactory,使用:
private fun initializeWorkManager (myInjectedParam: MyInjectedParam): WorkManager
{
val appContext = getApplication<MyApplication>()
val factory = appContext.workManagerConfiguration.workerFactory
as DelegatingWorkerFactory
factory.addFactory(MyWorkerFactory(myInjectedParam))
return WorkManager.getInstance(appContext)
}
在应用程序中 class 您有:
class MainApplication : Application(), Configuration.Provider {
val delegatingWorkerFactory: DelegatingWorkerFactory
// Setup custom configuration for WorkManager with a DelegatingWorkerFactory
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.setWorkerFactory(delegatingWorkerFactory)
.build()
}
}
如果您在库项目中有 Worker 和 WorkerFactory,则在库的清单中禁用默认初始化程序似乎可以解决问题。因为库将有一个不同于应用程序的包,默认初始化程序似乎被触发,除非你禁用它。
<provider android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="<library's package>.analytics.workmanager-init"
tools:node="remove" />
适合我的设置。
我的应用包含:
- Configuration/Provider
- DelegatingWorkerFactory() 被设置为上述配置
我的图书馆包含:
- 工人
- 工人工厂
- WorkRequestBuilder
- 正在排队工作请求
但让我感兴趣的是 WorkerFactory 源代码中的注释:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
不确定这是否应该警告我正在从不同的包调用此方法。
我正在编写一个使用 WorkManager 的 Android 库。在代码的某处,我这样调用:
val uploadTripRequest = OneTimeWorkRequest.Builder(UploadTripWorker::class.java)
.setConstraints(someConstraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(WORKER_NAME, ExistingWorkPolicy.REPLACE, uploadTripRequest)
在我的 build.gradle 我有:implementation 'androidx.work:work-runtime:2.2.0'
通常情况下,一切都很完美。客户端应用程序调用 https://developer.android.com/reference/kotlin/androidx/work/WorkManager.html#initialize 时出现问题,例如:
WorkManager.initialize(context, Configuration.Builder().setWorkerFactory(myWorkerFactory).build())
调用它时,我的库中的 WorkManager 没有按预期工作。 doWork
方法没有在我的 UploadTripWorker
上调用,但它在客户 myWorkerFactory
提供的 worker 上执行。
从我的角度来看,WorkManager
在库中是不可用的,因为它是一个单例并且有人可以通过 initialize
方法改变它的行为。但我希望我是错的。
有什么办法可以解决这个问题吗?当然,我不能对我所有的客户说不要使用 WorkManager.initialize()
方法。
WorkManager v2.1 引入了可以在这些情况下提供帮助的 DelegatingWorkerFactory
class。
仍然需要应用程序开发人员以正确的方式实现它,正如您所说的 WorkManager 是单例的。
关键是,如果应用程序需要自定义初始化和自定义 WorkerFactory,它应该使用 DelegatingWorkerFactory
然后 add 它自己的 WorkerFactory。
这里的关键点是应用程序的 WorkerFactory 应该检查 worker class 名称以确保它是正确的(通常在构造函数中注入一些参数)。如果 class 名称不是应用程序所期望的,它可以 return null 并且 DelegatingWorkerFactory
将注意找到正确的工人 class 来实例化。
类似于(在应用程序代码中):
class MyWorkerFactory(
private val myInjectedParam: InjectedParam
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return when (workerClassName) {
MyWorker::class.java.name ->
MyWorker(appContext, workerParameters, myInjectedParam)
else ->
// Return null, so that the base class can delegate to the default WorkerFactory.
null
}
}
}
然后,应用需要将此 WorkerFactory 添加到已设置为自定义 WorkerFactory 的 DelegatingWorkerFactory,使用:
private fun initializeWorkManager (myInjectedParam: MyInjectedParam): WorkManager
{
val appContext = getApplication<MyApplication>()
val factory = appContext.workManagerConfiguration.workerFactory
as DelegatingWorkerFactory
factory.addFactory(MyWorkerFactory(myInjectedParam))
return WorkManager.getInstance(appContext)
}
在应用程序中 class 您有:
class MainApplication : Application(), Configuration.Provider {
val delegatingWorkerFactory: DelegatingWorkerFactory
// Setup custom configuration for WorkManager with a DelegatingWorkerFactory
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.setWorkerFactory(delegatingWorkerFactory)
.build()
}
}
如果您在库项目中有 Worker 和 WorkerFactory,则在库的清单中禁用默认初始化程序似乎可以解决问题。因为库将有一个不同于应用程序的包,默认初始化程序似乎被触发,除非你禁用它。
<provider android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="<library's package>.analytics.workmanager-init"
tools:node="remove" />
适合我的设置。
我的应用包含:
- Configuration/Provider
- DelegatingWorkerFactory() 被设置为上述配置
我的图书馆包含:
- 工人
- 工人工厂
- WorkRequestBuilder
- 正在排队工作请求
但让我感兴趣的是 WorkerFactory 源代码中的注释:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
不确定这是否应该警告我正在从不同的包调用此方法。