Dagger2:无法在 WorkManager 中注入依赖项
Dagger2: Unable to inject dependencies in WorkManager
据我了解,Dagger 尚不支持在 Worker 中注入。但是正如人们所建议的那样,有一些解决方法。我尝试按照在线示例通过多种方式来完成此操作,但 none 对我有用。
当我不尝试向 Worker class 注入任何东西时,代码工作正常,只是我不能做我想做的事,因为我需要访问一些 DAO 和服务。如果我在这些依赖项上使用 @Inject,则依赖项要么为空,要么 worker 永远不会启动,即调试器甚至不进入 Worker class.
例如,我尝试这样做:
@Component(modules = {Module.class})
public interface Component{
void inject(MyWorker myWorker);
}
@Module
public class Module{
@Provides
public MyRepository getMyRepo(){
return new myRepository();
}
}
在我的工人中
@Inject
MyRepository myRepo;
public MyWorker() {
DaggerAppComponent.builder().build().inject(this);
}
但是执行永远不会到达工人。如果我删除构造函数,myRepo 依赖项将保持为空。
我尝试做很多其他事情,但 none 工作。有没有办法做到这一点?谢谢!!
在 WorkManager alpha09
中有一个新的 WorkerFactory,您可以使用它以您想要的方式初始化 Worker
。
- 使用接受
ApplicationContext
和 WorkerParams
的新 Worker
构造函数。
- 通过
Configuration
注册 WorkerFactory
的实现。
- 创建一个
configuration
并注册新创建的WorkerFactory
。
- 使用此配置初始化
WorkManager
(同时删除代表您初始化 WorkManager
的 ContentProvider
)。
您需要执行以下操作:
public DaggerWorkerFactory implements WorkerFactory {
@Nullable Worker createWorker(
@NonNull Context appContext,
@NonNull String workerClassName,
@NonNull WorkerParameters workerParameters) {
try {
Class<? extends Worker> workerKlass = Class.forName(workerClassName).asSubclass(Worker.class);
Constructor<? extends Worker> constructor =
workerKlass.getDeclaredConstructor(Context.class, WorkerParameters.class);
// This assumes that you are not using the no argument constructor
// and using the variant of the constructor that takes in an ApplicationContext
// and WorkerParameters. Use the new constructor to @Inject dependencies.
Worker instance = constructor.newInstance(appContext,workerParameters);
return instance;
} catch (Throwable exeption) {
Log.e("DaggerWorkerFactory", "Could not instantiate " + workerClassName, e);
// exception handling
return null;
}
}
}
// Create a configuration
Configuration configuration = new Configuration.Builder()
.setWorkerFactory(new DaggerWorkerFactory())
.build();
// Initialize WorkManager
WorkManager.initialize(context, configuration);
我用Dagger2 Multibindings来解决这个问题。
类似的方法用于注入 ViewModel
对象(描述得很好 )。与视图模型案例的重要区别是 Worker
构造函数中存在 Context
和 WorkerParameters
参数。要向 worker 构造函数提供这些参数,应使用中间 dagger 组件。
用 @Inject
注释 Worker
的构造函数,并提供所需的依赖项作为构造函数参数。
class HardWorker @Inject constructor(context: Context,
workerParams: WorkerParameters,
private val someDependency: SomeDependency)
: Worker(context, workerParams) {
override fun doWork(): Result {
// do some work with use of someDependency
return Result.SUCCESS
}
}
创建指定工作人员多绑定映射条目键的自定义注释。
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class WorkerKey(val value: KClass<out Worker>)
定义工作者绑定。
@Module
interface HardWorkerModule {
@Binds
@IntoMap
@WorkerKey(HardWorker::class)
fun bindHardWorker(worker: HardWorker): Worker
}
定义中间组件及其构建器。该组件必须具有从依赖图中获取工作人员映射的方法,并在其模块中包含工作人员绑定模块。此外,该组件必须声明为其父组件的子组件,并且父组件必须具有获取子组件构建器的方法。
typealias WorkerMap = MutableMap<Class<out Worker>, Provider<Worker>>
@Subcomponent(modules = [HardWorkerModule::class])
interface WorkerFactoryComponent {
fun workers(): WorkerMap
@Subcomponent.Builder
interface Builder {
@BindsInstance
fun setParameters(params: WorkerParameters): Builder
@BindsInstance
fun setContext(context: Context): Builder
fun build(): WorkerFactoryComponent
}
}
// parent component
@ParentComponentScope
@Component(modules = [
//, ...
])
interface ParentComponent {
// ...
fun workerFactoryComponent(): WorkerFactoryComponent.Builder
}
实施WorkerFactory
。它将创建中间组件,获取worker map,找到相应的worker provider并构造请求的worker。
class DIWorkerFactory(private val parentComponent: ParentComponent) : WorkerFactory() {
private fun createWorker(workerClassName: String, workers: WorkerMap): ListenableWorker? = try {
val workerClass = Class.forName(workerClassName).asSubclass(Worker::class.java)
var provider = workers[workerClass]
if (provider == null) {
for ((key, value) in workers) {
if (workerClass.isAssignableFrom(key)) {
provider = value
break
}
}
}
if (provider == null)
throw IllegalArgumentException("no provider found")
provider.get()
} catch (th: Throwable) {
// log
null
}
override fun createWorker(appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters) = parentComponent
.workerFactoryComponent()
.setContext(appContext)
.setParameters(workerParameters)
.build()
.workers()
.let { createWorker(workerClassName, it) }
}
使用自定义工作工厂手动初始化 WorkManager
(每个进程只能执行一次)。不要忘记在清单中禁用自动初始化。
清单:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
申请onCreate
:
val configuration = Configuration.Builder()
.setWorkerFactory(DIWorkerFactory(parentComponent))
.build()
WorkManager.initialize(context, configuration)
使用工人
val request = OneTimeWorkRequest.Builder(workerClass).build(HardWorker::class.java)
WorkManager.getInstance().enqueue(request)
观看此 talk 以了解有关 WorkManager
功能的更多信息。
概述
您需要查看 WorkerFactory,从 1.0.0-alpha09
开始可用。
以前的解决方法依赖于能够使用默认的 0-arg 构造函数创建 Worker
,但从 1.0.0-alpha10
开始,这不再是一个选项。
例子
假设您有一个名为 DataClearingWorker
的 Worker
subclass,并且此 class 需要来自您的 Dagger 图的 Foo
。
class DataClearingWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
lateinit var foo: Foo
override fun doWork(): Result {
foo.doStuff()
return Result.SUCCESS
}
}
现在,您不能直接实例化其中一个 DataClearingWorker
实例。所以你需要定义一个 WorkerFactory
subclass 可以为你创建其中一个;不仅要创建一个,还要设置 Foo
字段。
class DaggerWorkerFactory(private val foo: Foo) : WorkerFactory() {
override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? {
val workerKlass = Class.forName(workerClassName).asSubclass(Worker::class.java)
val constructor = workerKlass.getDeclaredConstructor(Context::class.java, WorkerParameters::class.java)
val instance = constructor.newInstance(appContext, workerParameters)
when (instance) {
is DataClearingWorker -> {
instance.foo = foo
}
// optionally, handle other workers
}
return instance
}
}
最后,您需要创建一个可以访问 Foo
的 DaggerWorkerFactory
。您可以使用普通 Dagger 方式执行此操作。
@Provides
@Singleton
fun workerFactory(foo: Foo): WorkerFactory {
return DaggerWorkerFactory(foo)
}
禁用默认 WorkManager 初始化
您还需要禁用默认 WorkManager
初始化(自动发生)并手动初始化。
如何执行此操作取决于您使用的 androidx.work
版本:
2.6.0 及更高版本:
在AndroidManifest.xml
中添加:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="YOUR_APP_PACKAGE.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
2.6.0 之前:
在AndroidManifest.xml
中添加:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="YOUR_APP_PACKAGE.workmanager-init"
android:enabled="false"
android:exported="false"
tools:replace="android:authorities" />
请务必将 YOUR_APP_PACKAGE 替换为您实际的应用程序包。上面的 <provider
块进入 inside 你的 <application
标签..所以它是你的 Activities
, Services
等的兄弟...
在你的 Application
subclass,(或者你喜欢的其他地方),你可以手动初始化 WorkManager
.
@Inject
lateinit var workerFactory: WorkerFactory
private fun configureWorkManager() {
val config = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
WorkManager.initialize(this, config)
}
2020/06更新
有了 Hilt and Hilt for Jetpack,事情变得容易多了。
有了 Hilt,您所要做的就是
- 向您的应用程序添加注释
@HiltAndroidApp
class
- 在应用程序 class
字段中注入开箱即用 HiltWorkerFactory
- 实现接口
Configuration.Provider
和return步骤2中注入的工作工厂。
现在,将Worker的构造函数上的注解从@Inject
改为@WorkerInject
class ExampleWorker @WorkerInject constructor(
@Assisted appContext: Context,
@Assisted workerParams: WorkerParameters,
someDependency: SomeDependency // your own dependency
) : Worker(appContext, workerParams) { ... }
就是这样!
(另外,不要忘记禁用默认工作管理器初始化)
===========
旧解
从版本 1.0.0-beta01 开始,这里是使用 WorkerFactory 实现 Dagger 注入。
概念来自这篇文章: https://medium.com/@nlg.tuan.kiet/bb9f474bde37 而我只是post自己一步一步实现它(在科特林).
===========
此实现试图实现的目标是:
每次要给一个worker添加依赖,就把依赖放在相关的worker中class
===========
1.为所有工人工厂添加接口
IWorkerFactory.kt
interface IWorkerFactory<T : ListenableWorker> {
fun create(params: WorkerParameters): T
}
2. 添加一个简单的 Worker class 和一个 实现 IWorkerFactory[=130 的 Factory =] 以及这个工人的依赖性
HelloWorker.kt
class HelloWorker(
context: Context,
params: WorkerParameters,
private val apiService: ApiService // our dependency
): Worker(context, params) {
override fun doWork(): Result {
Log.d("HelloWorker", "doWork - fetchSomething")
return apiService.fetchSomething() // using Retrofit + RxJava
.map { Result.success() }
.onErrorReturnItem(Result.failure())
.blockingGet()
}
class Factory @Inject constructor(
private val context: Provider<Context>, // provide from AppModule
private val apiService: Provider<ApiService> // provide from NetworkModule
) : IWorkerFactory<HelloWorker> {
override fun create(params: WorkerParameters): HelloWorker {
return HelloWorker(context.get(), params, apiService.get())
}
}
}
3.为Dagger的multi-binding
添加一个WorkerKey
WorkerKey.kt
@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
4.为multi-binding worker添加一个Dagger模块(实际上是multi-binding factory)
WorkerModule.kt
@Module
interface WorkerModule {
@Binds
@IntoMap
@WorkerKey(HelloWorker::class)
fun bindHelloWorker(factory: HelloWorker.Factory): IWorkerFactory<out ListenableWorker>
// every time you add a worker, add a binding here
}
5. 将WorkerModule放入AppComponent。这里我使用dagger-android构造组件class
AppComponent.kt
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
NetworkModule::class, // provides ApiService
AppModule::class, // provides context of application
WorkerModule::class // <- add WorkerModule here
])
interface AppComponent: AndroidInjector<App> {
@Component.Builder
abstract class Builder: AndroidInjector.Builder<App>()
}
6. 添加自定义 WorkerFactory 以利用自 1.0.0-alpha09[=] 发布版本以来创建 worker 的能力26=]
DaggerAwareWorkerFactory.kt
class DaggerAwareWorkerFactory @Inject constructor(
private val workerFactoryMap: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<IWorkerFactory<out ListenableWorker>>>
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
val entry = workerFactoryMap.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) }
val factory = entry?.value
?: throw IllegalArgumentException("could not find worker: $workerClassName")
return factory.get().create(workerParameters)
}
}
7. 在应用程序 class 中,将 WorkerFactory 替换为我们自定义的:
App.kt
class App: DaggerApplication() {
override fun onCreate() {
super.onCreate()
configureWorkManager()
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
@Inject lateinit var daggerAwareWorkerFactory: DaggerAwareWorkerFactory
private fun configureWorkManager() {
val config = Configuration.Builder()
.setWorkerFactory(daggerAwareWorkerFactory)
.build()
WorkManager.initialize(this, config)
}
}
8.不要忘记禁用默认工作管理器初始化
AndroidManifest.xml
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:enabled="false"
android:exported="false"
tools:replace="android:authorities" />
就是这样。
每次你想给一个worker添加一个依赖,你把依赖放在相关的worker中class(比如这里的HelloWorker)。
每次要添加worker时,在worker中实现factoryclass,将worker的factory添加到WorkerModule中进行多绑定
更多细节,比如使用AssistedInject减少样板代码,请参考我开头提到的文章。
据我了解,Dagger 尚不支持在 Worker 中注入。但是正如人们所建议的那样,有一些解决方法。我尝试按照在线示例通过多种方式来完成此操作,但 none 对我有用。
当我不尝试向 Worker class 注入任何东西时,代码工作正常,只是我不能做我想做的事,因为我需要访问一些 DAO 和服务。如果我在这些依赖项上使用 @Inject,则依赖项要么为空,要么 worker 永远不会启动,即调试器甚至不进入 Worker class.
例如,我尝试这样做:
@Component(modules = {Module.class})
public interface Component{
void inject(MyWorker myWorker);
}
@Module
public class Module{
@Provides
public MyRepository getMyRepo(){
return new myRepository();
}
}
在我的工人中
@Inject
MyRepository myRepo;
public MyWorker() {
DaggerAppComponent.builder().build().inject(this);
}
但是执行永远不会到达工人。如果我删除构造函数,myRepo 依赖项将保持为空。
我尝试做很多其他事情,但 none 工作。有没有办法做到这一点?谢谢!!
在 WorkManager alpha09
中有一个新的 WorkerFactory,您可以使用它以您想要的方式初始化 Worker
。
- 使用接受
ApplicationContext
和WorkerParams
的新Worker
构造函数。 - 通过
Configuration
注册WorkerFactory
的实现。 - 创建一个
configuration
并注册新创建的WorkerFactory
。 - 使用此配置初始化
WorkManager
(同时删除代表您初始化WorkManager
的ContentProvider
)。
您需要执行以下操作:
public DaggerWorkerFactory implements WorkerFactory {
@Nullable Worker createWorker(
@NonNull Context appContext,
@NonNull String workerClassName,
@NonNull WorkerParameters workerParameters) {
try {
Class<? extends Worker> workerKlass = Class.forName(workerClassName).asSubclass(Worker.class);
Constructor<? extends Worker> constructor =
workerKlass.getDeclaredConstructor(Context.class, WorkerParameters.class);
// This assumes that you are not using the no argument constructor
// and using the variant of the constructor that takes in an ApplicationContext
// and WorkerParameters. Use the new constructor to @Inject dependencies.
Worker instance = constructor.newInstance(appContext,workerParameters);
return instance;
} catch (Throwable exeption) {
Log.e("DaggerWorkerFactory", "Could not instantiate " + workerClassName, e);
// exception handling
return null;
}
}
}
// Create a configuration
Configuration configuration = new Configuration.Builder()
.setWorkerFactory(new DaggerWorkerFactory())
.build();
// Initialize WorkManager
WorkManager.initialize(context, configuration);
我用Dagger2 Multibindings来解决这个问题。
类似的方法用于注入 ViewModel
对象(描述得很好 Worker
构造函数中存在 Context
和 WorkerParameters
参数。要向 worker 构造函数提供这些参数,应使用中间 dagger 组件。
用
@Inject
注释Worker
的构造函数,并提供所需的依赖项作为构造函数参数。class HardWorker @Inject constructor(context: Context, workerParams: WorkerParameters, private val someDependency: SomeDependency) : Worker(context, workerParams) { override fun doWork(): Result { // do some work with use of someDependency return Result.SUCCESS } }
创建指定工作人员多绑定映射条目键的自定义注释。
@MustBeDocumented @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) @Retention(AnnotationRetention.RUNTIME) @MapKey annotation class WorkerKey(val value: KClass<out Worker>)
定义工作者绑定。
@Module interface HardWorkerModule { @Binds @IntoMap @WorkerKey(HardWorker::class) fun bindHardWorker(worker: HardWorker): Worker }
定义中间组件及其构建器。该组件必须具有从依赖图中获取工作人员映射的方法,并在其模块中包含工作人员绑定模块。此外,该组件必须声明为其父组件的子组件,并且父组件必须具有获取子组件构建器的方法。
typealias WorkerMap = MutableMap<Class<out Worker>, Provider<Worker>> @Subcomponent(modules = [HardWorkerModule::class]) interface WorkerFactoryComponent { fun workers(): WorkerMap @Subcomponent.Builder interface Builder { @BindsInstance fun setParameters(params: WorkerParameters): Builder @BindsInstance fun setContext(context: Context): Builder fun build(): WorkerFactoryComponent } } // parent component @ParentComponentScope @Component(modules = [ //, ... ]) interface ParentComponent { // ... fun workerFactoryComponent(): WorkerFactoryComponent.Builder }
实施
WorkerFactory
。它将创建中间组件,获取worker map,找到相应的worker provider并构造请求的worker。class DIWorkerFactory(private val parentComponent: ParentComponent) : WorkerFactory() { private fun createWorker(workerClassName: String, workers: WorkerMap): ListenableWorker? = try { val workerClass = Class.forName(workerClassName).asSubclass(Worker::class.java) var provider = workers[workerClass] if (provider == null) { for ((key, value) in workers) { if (workerClass.isAssignableFrom(key)) { provider = value break } } } if (provider == null) throw IllegalArgumentException("no provider found") provider.get() } catch (th: Throwable) { // log null } override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters) = parentComponent .workerFactoryComponent() .setContext(appContext) .setParameters(workerParameters) .build() .workers() .let { createWorker(workerClassName, it) } }
使用自定义工作工厂手动初始化
WorkManager
(每个进程只能执行一次)。不要忘记在清单中禁用自动初始化。
清单:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
申请onCreate
:
val configuration = Configuration.Builder()
.setWorkerFactory(DIWorkerFactory(parentComponent))
.build()
WorkManager.initialize(context, configuration)
使用工人
val request = OneTimeWorkRequest.Builder(workerClass).build(HardWorker::class.java) WorkManager.getInstance().enqueue(request)
观看此 talk 以了解有关 WorkManager
功能的更多信息。
概述
您需要查看 WorkerFactory,从 1.0.0-alpha09
开始可用。
以前的解决方法依赖于能够使用默认的 0-arg 构造函数创建 Worker
,但从 1.0.0-alpha10
开始,这不再是一个选项。
例子
假设您有一个名为 DataClearingWorker
的 Worker
subclass,并且此 class 需要来自您的 Dagger 图的 Foo
。
class DataClearingWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
lateinit var foo: Foo
override fun doWork(): Result {
foo.doStuff()
return Result.SUCCESS
}
}
现在,您不能直接实例化其中一个 DataClearingWorker
实例。所以你需要定义一个 WorkerFactory
subclass 可以为你创建其中一个;不仅要创建一个,还要设置 Foo
字段。
class DaggerWorkerFactory(private val foo: Foo) : WorkerFactory() {
override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? {
val workerKlass = Class.forName(workerClassName).asSubclass(Worker::class.java)
val constructor = workerKlass.getDeclaredConstructor(Context::class.java, WorkerParameters::class.java)
val instance = constructor.newInstance(appContext, workerParameters)
when (instance) {
is DataClearingWorker -> {
instance.foo = foo
}
// optionally, handle other workers
}
return instance
}
}
最后,您需要创建一个可以访问 Foo
的 DaggerWorkerFactory
。您可以使用普通 Dagger 方式执行此操作。
@Provides
@Singleton
fun workerFactory(foo: Foo): WorkerFactory {
return DaggerWorkerFactory(foo)
}
禁用默认 WorkManager 初始化
您还需要禁用默认 WorkManager
初始化(自动发生)并手动初始化。
如何执行此操作取决于您使用的 androidx.work
版本:
2.6.0 及更高版本:
在AndroidManifest.xml
中添加:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="YOUR_APP_PACKAGE.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
2.6.0 之前:
在AndroidManifest.xml
中添加:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="YOUR_APP_PACKAGE.workmanager-init"
android:enabled="false"
android:exported="false"
tools:replace="android:authorities" />
请务必将 YOUR_APP_PACKAGE 替换为您实际的应用程序包。上面的 <provider
块进入 inside 你的 <application
标签..所以它是你的 Activities
, Services
等的兄弟...
在你的 Application
subclass,(或者你喜欢的其他地方),你可以手动初始化 WorkManager
.
@Inject
lateinit var workerFactory: WorkerFactory
private fun configureWorkManager() {
val config = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
WorkManager.initialize(this, config)
}
2020/06更新
有了 Hilt and Hilt for Jetpack,事情变得容易多了。
有了 Hilt,您所要做的就是
- 向您的应用程序添加注释
@HiltAndroidApp
class - 在应用程序 class 字段中注入开箱即用
- 实现接口
Configuration.Provider
和return步骤2中注入的工作工厂。
HiltWorkerFactory
现在,将Worker的构造函数上的注解从@Inject
改为@WorkerInject
class ExampleWorker @WorkerInject constructor(
@Assisted appContext: Context,
@Assisted workerParams: WorkerParameters,
someDependency: SomeDependency // your own dependency
) : Worker(appContext, workerParams) { ... }
就是这样!
(另外,不要忘记禁用默认工作管理器初始化)
===========
旧解
从版本 1.0.0-beta01 开始,这里是使用 WorkerFactory 实现 Dagger 注入。
概念来自这篇文章: https://medium.com/@nlg.tuan.kiet/bb9f474bde37 而我只是post自己一步一步实现它(在科特林).
===========
此实现试图实现的目标是:
每次要给一个worker添加依赖,就把依赖放在相关的worker中class
===========
1.为所有工人工厂添加接口
IWorkerFactory.kt
interface IWorkerFactory<T : ListenableWorker> {
fun create(params: WorkerParameters): T
}
2. 添加一个简单的 Worker class 和一个 实现 IWorkerFactory[=130 的 Factory =] 以及这个工人的依赖性
HelloWorker.kt
class HelloWorker(
context: Context,
params: WorkerParameters,
private val apiService: ApiService // our dependency
): Worker(context, params) {
override fun doWork(): Result {
Log.d("HelloWorker", "doWork - fetchSomething")
return apiService.fetchSomething() // using Retrofit + RxJava
.map { Result.success() }
.onErrorReturnItem(Result.failure())
.blockingGet()
}
class Factory @Inject constructor(
private val context: Provider<Context>, // provide from AppModule
private val apiService: Provider<ApiService> // provide from NetworkModule
) : IWorkerFactory<HelloWorker> {
override fun create(params: WorkerParameters): HelloWorker {
return HelloWorker(context.get(), params, apiService.get())
}
}
}
3.为Dagger的multi-binding
添加一个WorkerKeyWorkerKey.kt
@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
4.为multi-binding worker添加一个Dagger模块(实际上是multi-binding factory)
WorkerModule.kt
@Module
interface WorkerModule {
@Binds
@IntoMap
@WorkerKey(HelloWorker::class)
fun bindHelloWorker(factory: HelloWorker.Factory): IWorkerFactory<out ListenableWorker>
// every time you add a worker, add a binding here
}
5. 将WorkerModule放入AppComponent。这里我使用dagger-android构造组件class
AppComponent.kt
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
NetworkModule::class, // provides ApiService
AppModule::class, // provides context of application
WorkerModule::class // <- add WorkerModule here
])
interface AppComponent: AndroidInjector<App> {
@Component.Builder
abstract class Builder: AndroidInjector.Builder<App>()
}
6. 添加自定义 WorkerFactory 以利用自 1.0.0-alpha09[=] 发布版本以来创建 worker 的能力26=]
DaggerAwareWorkerFactory.kt
class DaggerAwareWorkerFactory @Inject constructor(
private val workerFactoryMap: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<IWorkerFactory<out ListenableWorker>>>
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
val entry = workerFactoryMap.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) }
val factory = entry?.value
?: throw IllegalArgumentException("could not find worker: $workerClassName")
return factory.get().create(workerParameters)
}
}
7. 在应用程序 class 中,将 WorkerFactory 替换为我们自定义的:
App.kt
class App: DaggerApplication() {
override fun onCreate() {
super.onCreate()
configureWorkManager()
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
@Inject lateinit var daggerAwareWorkerFactory: DaggerAwareWorkerFactory
private fun configureWorkManager() {
val config = Configuration.Builder()
.setWorkerFactory(daggerAwareWorkerFactory)
.build()
WorkManager.initialize(this, config)
}
}
8.不要忘记禁用默认工作管理器初始化
AndroidManifest.xml
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:enabled="false"
android:exported="false"
tools:replace="android:authorities" />
就是这样。
每次你想给一个worker添加一个依赖,你把依赖放在相关的worker中class(比如这里的HelloWorker)。
每次要添加worker时,在worker中实现factoryclass,将worker的factory添加到WorkerModule中进行多绑定
更多细节,比如使用AssistedInject减少样板代码,请参考我开头提到的文章。