2020/06 更新
Hilt 和 Hilt for Jetpack 让事情变得容易得多。
有了 Hilt,你所要做的就是
- 将注释
@HiltAndroidApp 添加到您的应用程序类
- 在应用程序类的字段中注入开箱即用的
HiltWorkerFactory
- 实现接口
Configuration.Provider并返回步骤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,我只是逐步发布我自己的实现(在 Kotlin 中)。
============
这个实现试图实现的是:
每次你想给一个worker添加一个依赖,你把依赖放到相关的worker类中
============
1.为所有工人的工厂添加一个接口
IWorkerFactory.kt
interface IWorkerFactory<T : ListenableWorker> {
fun create(params: WorkerParameters): T
}
2. 添加一个简单的 Worker 类,其中包含一个 Factory,该类实现了 IWorkerFactory,并且还具有该 worker 的依赖项
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的多重绑定
添加一个
WorkerKey
WorkerKey.kt
@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
4.为多绑定worker添加一个Dagger模块(实际上是多绑定工厂)
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来构造组件类
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 发布版本以来创建工人的能力
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. 在 Application 类中,将 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类中(比如这里的HelloWorker)。
每次要添加worker时,在worker类中实现工厂,将worker的工厂添加到WorkerModule中进行多绑定。
更多细节,如使用 AssistedInject 减少样板代码,请参考我开头提到的文章。