Dagger2:无法在WorkManager中注入依赖项

时间:2018-09-20 22:24:49

标签: android dagger-2 android-workmanager

因此,据我所读,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依赖项将保持为空。

我尝试做许多其他事情,但没有任何效果。有没有办法做到这一点?谢谢!

4 个答案:

答案 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

禁用默认的WorkManager初始化

您还需要禁用默认的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构造函数,该构造函数接受ApplicationContextWorkerParams
  • 通过WorkerFactory注册Configuration的实现。
  • 创建一个configuration并注册新创建的WorkerFactory
  • 使用此配置初始化WorkManager(同时删除代表您初始化ContentProvider的{​​{1}})。

您需要执行以下操作:

WorkManager

答案 3 :(得分:3)

我使用Dagger2 Multibindings解决了这个问题。

使用类似的方法来注入ViewModel对象(已很好地描述了here)。与视图模型案例的重要区别是Context构造函数中存在WorkerParametersWorker自变量。要将这些参数提供给worker构造函数,应使用中间的匕首组件。

  1. 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
        }
    }
    
  2. 创建自定义注释,该注释为工作程序多绑定映射条目指定密钥。

    @MustBeDocumented
    @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
    @Retention(AnnotationRetention.RUNTIME)
    @MapKey
    annotation class WorkerKey(val value: KClass<out Worker>)
    
  3. 定义工作程序绑定。

    @Module
    interface HardWorkerModule {
    
        @Binds
        @IntoMap
        @WorkerKey(HardWorker::class)
        fun bindHardWorker(worker: HardWorker): Worker
    }
    
  4. 定义中间组件以及其构建器和模块,该构建器和模块将提供ContextWorkerParameters对象。组件必须具有从依赖关系图获取工作人员映射并在其模块之间包含工作人员绑定模块的方法。此外,必须将该组件声明为其父组件的子组件,并且父组件必须具有获取子组件的构建器的方法。

    @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
    }
    
  5. 实施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()) }
    }
    
  6. 使用自定义工作程序工厂手动初始化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)
  1. 使用工人

    val request = OneTimeWorkRequest.Builder(workerClass).build(HardWorker::class.java)
    WorkManager.getInstance().enqueue(request)
    

观看此talk,以获取有关WorkManager功能的更多信息。