因此,据我所读,Dagger还不支持在Worker中注入。但是,正如人们建议的那样,有一些解决方法。我已经尝试了多种方法来按照网上示例进行操作,但是没有一种对我有用。
当我不尝试向Worker类中注入任何内容时,代码可以正常工作,仅是因为我需要访问某些DAO和服务而无法做我想做的事情。如果我在这些依赖项上使用@Inject,则依赖项为null或工作程序从不启动,即调试器甚至都没有进入Worker类。
例如,我尝试这样做:
@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依赖项将保持为空。
我尝试做许多其他事情,但没有任何效果。有没有办法做到这一点?谢谢!
答案 0 :(得分:17)
您需要查看1.0.0-alpha09
起的WorkerFactory。
以前的解决方法依赖于能够使用默认的0-arg构造函数创建Worker
,但是从1.0.0-alpha10
开始,这不再是一个选择。
假设您有一个名为Worker
的{{1}}子类,并且该类需要Dagger图中的DataClearingWorker
。
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
字段。
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
}
}
的{{1}}。您可以使用 normal Dagger方式进行操作。
DaggerWorkerFactory
您还需要禁用默认的Foo
初始化(自动发生)并手动对其进行初始化。
在@Provides
@Singleton
fun workerFactory(foo: Foo): WorkerFactory {
return DaggerWorkerFactory(foo)
}
中,您可以这样禁用它:
WorkManager
请确保将 com.your.app.package 替换为实际应用的软件包。上方的AndroidManifest.xml
块位于您的 <provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="com.your.app.package.workmanager-init"
android:enabled="false"
android:exported="false"
tools:replace="android:authorities" />
标签的内部内。因此,这是您的<provider
,<application
等的同级对象。
在您的Activities
子类中(或您愿意的其他地方),您可以手动初始化Services
。
Application
答案 1 :(得分:4)
从版本 1.0.0-beta01 开始,这是使用WorkerFactory进行Dagger注入的实现。
这个概念来自本文:https://medium.com/@nlg.tuan.kiet/bb9f474bde37,我只是一步一步地发布了自己的实现(在Kotlin中)。
===========
此实现要实现的目标是:
每次要向工作程序添加依赖项时,都将依赖项放入相关的工作程序类中
===========
1。。为所有工人的工厂添加界面
IWorkerFactory.kt
interface IWorkerFactory<T : ListenableWorker> {
fun create(params: WorkerParameters): T
}
2。。添加带有工厂的简单 Worker类,该类实现IWorkerFactory ,并具有该工作者的依赖性
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。。为多重绑定工作者(实际上是对工厂进行多重绑定)添加了一个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。。自1.0.0-alpha09发行版以来,添加了自定义WorkerFactory以利用创建worker 的功能。
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" />
就是这样。
每次要向工作程序添加依赖项时,都将依赖项放入相关的工作程序类中(例如此处的HelloWorker)。
每次要添加工作程序时,都需要在worker类中实现工厂,并将该工作程序的工厂添加到WorkerModule以进行多重绑定。
有关更多详细信息,例如使用AssistedInject减少样板代码,请参阅我在开始时提到的文章。
答案 2 :(得分:3)
在WorkManager alpha09
中,有一个新的WorkerFactory,可用于初始化所需的Worker
。
Worker
构造函数,该构造函数接受ApplicationContext
和WorkerParams
。WorkerFactory
注册Configuration
的实现。configuration
并注册新创建的WorkerFactory
。WorkManager
(同时删除代表您初始化ContentProvider
的{{1}})。您需要执行以下操作:
WorkManager
答案 3 :(得分:3)
我使用Dagger2 Multibindings解决了这个问题。
使用类似的方法来注入ViewModel
对象(已很好地描述了here)。与视图模型案例的重要区别是Context
构造函数中存在WorkerParameters
和Worker
自变量。要将这些参数提供给worker构造函数,应使用中间的匕首组件。
用Worker
注释@Inject
的构造函数,并提供所需的依赖项作为构造函数参数。
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
}
定义中间组件以及其构建器和模块,该构建器和模块将提供Context
和WorkerParameters
对象。组件必须具有从依赖关系图获取工作人员映射并在其模块之间包含工作人员绑定模块的方法。此外,必须将该组件声明为其父组件的子组件,并且父组件必须具有获取子组件的构建器的方法。
@Module
class ArgumentsModule(private val appContext: Context,
private val workerParameters: WorkerParameters) {
@Provides
fun provideAppContext() = appContext
@Provides
fun provideWorkerParameters() = workerParameters
}
typealias WorkerMap = MutableMap<Class<out Worker>, Provider<Worker>>
@Subcomponent(modules = [
ArgumentsModule::class,
HardWorkerModule::class])
interface WorkerFactoryComponent {
fun workers(): WorkerMap
@Subcomponent.Builder
interface Builder {
fun argumentsModule(module: ArgumentsModule): Builder
fun build(): WorkerFactoryComponent
}
}
// some module of the parent component
@Module(subcomponents = [WorkerFactoryComponent::class
//, ...
])
class ParentComponentModule {
// ...
}
// parent component
@ParentComponentScope
@Component(modules = [ParentComponentModule::class
//, ...
])
interface ParentComponent {
// ...
fun workerFactoryComponent(): WorkerFactoryComponent.Builder
}
实施WorkerFactory
。它将创建中间组件,获取工作人员图,找到相应的工作人员提供者并构造所请求的工作人员。
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()
.argumentsModule(ArgumentsModule(appContext, workerParameters))
.build()
.run { createWorker(workerClassName, workers()) }
}
使用自定义工作程序工厂手动初始化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
功能的更多信息。