Android Dagger2:无法在WorkManager中注入依赖项
所以从我读到的,Dagger还不支持InjectinWorker。但正如人们所建议的,还有一些变通办法。我在网上尝试了很多方法,但都不适合我 当我不尝试向Worker类中注入任何内容时,代码工作正常,只是我不能做我想做的事情,因为我需要访问一些DAO和服务。如果我在这些依赖项上使用@Inject,则依赖项要么为null,要么worker从未启动,即调试器甚至没有进入worker类 例如,我试着这样做:Android Dagger2:无法在WorkManager中注入依赖项,android,dagger-2,android-workmanager,Android,Dagger 2,Android Workmanager,所以从我读到的,Dagger还不支持InjectinWorker。但正如人们所建议的,还有一些变通办法。我在网上尝试了很多方法,但都不适合我 当我不尝试向Worker类中注入任何内容时,代码工作正常,只是我不能做我想做的事情,因为我需要访问一些DAO和服务。如果我在这些依赖项上使用@Inject,则依赖项要么为null,要么worker从未启动,即调试器甚至没有进入worker类 例如,我试着这样做: @Component(modules = {Module.class}) public int
@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依赖项将保持为null
我试过做很多其他的事情,但都没有成功。有没有办法做到这一点?谢谢 在WorkManager
alpha09
中,有一个新选项,您可以使用该选项以您想要的方式初始化工作人员
- 使用新的
Worker
构造函数,该构造函数接受ApplicationContext
和WorkerParams
- 通过
Configuration
注册WorkerFactory
的实现
- 创建
配置
并注册新创建的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);
公共DaggerWorkerFactory实现WorkerFactory{
@可为空的工作者createWorker(
@非空上下文appContext,
@非空字符串workerClassName,
@非空WorkerParameters(WorkerParameters){
试一试{
Class我用来解决这个问题
类似的方法用于注入ViewModel
对象(描述得很好)。与视图模型案例的重要区别在于Context
和WorkerParameters
参数在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>)
定义中间组件及其生成器。该组件必须具有从依赖关系图获取辅助对象映射的方法,并在其模块中包含辅助对象绑定模块。此外,该组件必须声明为其父组件的子组件,并且父组件必须具有获取子组件生成器的方法
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
}
使用自定义worker factory手动初始化WorkManager
(每个进程只能执行一次)。不要忘记在清单中禁用自动初始化
舱单:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
使用工人
val request = OneTimeWorkRequest.Builder(workerClass).build(HardWorker::class.java)
WorkManager.getInstance().enqueue(request)
有关WorkManager
功能的更多信息,请观看本节。概述
您需要查看,从1.0.0-alpha09
开始提供
以前的解决方法依赖于能够使用默认的0-arg构造函数创建Worker
,但从1.0.0-alpha10
开始,这不再是一个选项
例子
假设您有一个名为DataClearingWorker
的Worker
子类,这个类需要从匕首图中得到一个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
}
}
最后,您需要创建一个DaggerWorkerFactory
,该工厂可以访问Foo
。您可以使用普通的Dagger方法来完成此操作
@Provides
@Singleton
fun workerFactory(foo: Foo): WorkerFactory {
return DaggerWorkerFactory(foo)
}
禁用默认WorkManager初始化
您还需要禁用默认的WorkManager
初始化(自动发生)并手动初始化
在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" />
2020/06更新
随着时间的推移,事情变得容易多了
有了剑柄,你所要做的就是
向应用程序类添加注释@HiltAndroidApp
在应用程序类字段中注入开箱即用HiltWorkerFactory
执行接口配置。提供程序并在步骤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实现匕首注射
这个概念来自本文:我只是一步一步地发布我自己的实现(在Kotlin)
===========
此实施试图实现的目标是:
每次要向工作程序添加依赖项时,都将该依赖项放入相关的工作程序类中
===========
1.为所有工人的工厂添加一个界面
IWorkerFactory.kt
interface IWorkerFactory<T : ListenableWorker> {
fun create(params: WorkerParameters): T
}
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())
}
}
}
@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
@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
}
@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>()
}
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)
}
}
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)
}
}
界面IWorkerFactory{
乐趣创建(参数:WorkerParameters):T
}
2.添加一个简单的工人类,其中包含一个工厂,该工厂实现了IWorkerFactory,并且还包含此工人的依赖项
HelloWorker.kt
interface IWorkerFactory<T : ListenableWorker> {
fun create(params: WorkerParameters): T
}
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())
}
}
}
@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
@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
}
@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>()
}
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)
}
}
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)
}
}
类HelloWorker(
上下文:上下文,
参数:WorkerParameters,
private val apiService:apiService//我们的依赖关系
):工人(