var*_*nkr 15 android dagger-2 android-workmanager
所以从我读到的,Dagger还没有支持注入Worker.但是人们建议有一些解决方法.我尝试过多种方式在线跟踪示例,但它们都不适用于我.
当我不尝试向Worker类注入任何东西时,代码工作正常,只是我不能做我想要的,因为我需要访问一些DAO和服务.如果我对这些依赖项使用@Inject,则依赖项为null或者worker永远不会启动,即调试器甚至不进入Worker类.
例如,我尝试这样做:
@Component(modules = {Module.class})
public interface Component{
void inject(MyWorker myWorker);
}
@Module
public class Module{
@Provides
public MyRepository getMyRepo(){
return new myRepository();
}
}
Run Code Online (Sandbox Code Playgroud)
在我的工人
@Inject
MyRepository myRepo;
public MyWorker() {
DaggerAppComponent.builder().build().inject(this);
}
Run Code Online (Sandbox Code Playgroud)
但随后执行从未到达工人.如果我删除构造函数,myRepo依赖项将保持为null.
我尝试过很多其他事情,但没有工作.有没有办法做到这一点?谢谢!!
Cra*_*ell 22
您需要查看从此开始提供的WorkerFactory1.0.0-alpha09.
以前的变通方法依赖于能够Worker使用默认的0-arg构造函数创建一个,但是1.0.0-alpha10不再是一个选项.
假设你有一个Worker被调用的子类DataClearingWorker,并且这个类需要Foo来自你的Dagger图.
class DataClearingWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
lateinit var foo: Foo
override fun doWork(): Result {
foo.doStuff()
return Result.SUCCESS
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您不能直接实例化其中一个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
}
}
Run Code Online (Sandbox Code Playgroud)
最后,您需要创建一个DaggerWorkerFactory可以访问的权限Foo.你可以用普通的 Dagger方式做到这一点.
@Provides
@Singleton
fun workerFactory(foo: Foo): WorkerFactory {
return DaggerWorkerFactory(foo)
}
Run Code Online (Sandbox Code Playgroud)
您还需要禁用默认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" />
Run Code Online (Sandbox Code Playgroud)
请务必将com.your.app.package替换为您的实际应用程序包.在<provider上述块进入里面的<application标签..所以这是你的兄弟姐妹Activities,Services等等...
在您的Application子类中(或者您喜欢的其他位置),您可以手动初始化WorkManager.
@Inject
lateinit var workerFactory: WorkerFactory
private fun configureWorkManager() {
val config = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
WorkManager.initialize(this, config)
}
Run Code Online (Sandbox Code Playgroud)
从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
}
Run Code Online (Sandbox Code Playgroud)
2.添加一个简单的Worker类,其中包含一个实现IWorkerFactory 的Factory以及该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())
}
}
}
Run Code Online (Sandbox Code Playgroud)
3.为Dagger的多重绑定添加一个WorkerKey
WorkerKey.kt
@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
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>()
}
Run Code Online (Sandbox Code Playgroud)
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)
}
}
Run Code Online (Sandbox Code Playgroud)
7.在Application类中,用我们的自定义变量替换WorkerFactory:
应用程式
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)
}
}
Run Code Online (Sandbox Code Playgroud)
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" />
Run Code Online (Sandbox Code Playgroud)
而已。
每次您要向工作程序添加依赖项时,都将其放入相关的工作程序类中(例如此处的HelloWorker)。
每次您要添加一个工作器时,都在worker类中实现工厂,并将该工作器的工厂添加到WorkerModule中以进行多重绑定。
有关更多详细信息,例如使用AssistedInject减少样板代码,请参阅我在开始时提到的文章。
我使用Dagger2 多重绑定来解决这个问题。
类似的方法用于注入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
}
}
Run Code Online (Sandbox Code Playgroud)创建自定义注释,指定工作线程多绑定映射条目的键。
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class WorkerKey(val value: KClass<out Worker>)
Run Code Online (Sandbox Code Playgroud)定义工人绑定。
@Module
interface HardWorkerModule {
@Binds
@IntoMap
@WorkerKey(HardWorker::class)
fun bindHardWorker(worker: HardWorker): Worker
}
Run Code Online (Sandbox Code Playgroud)定义中间组件及其构建器。组件必须具有从依赖图中获取工作人员映射的方法,并在其模块中包含工作人员绑定模块。此外,组件必须声明为其父组件的子组件,并且父组件必须具有获取子组件构建器的方法。
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
}
Run Code Online (Sandbox Code Playgroud)实施WorkerFactory。它将创建中间组件,获取 worker 映射,找到相应的 worker 提供者并构造请求的 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) }
}
Run Code Online (Sandbox Code Playgroud)WorkManager使用自定义工人工厂手动初始化(每个进程必须仅完成一次)。不要忘记在清单中禁用自动初始化。
显现:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
Run Code Online (Sandbox Code Playgroud)
应用onCreate:
val configuration = Configuration.Builder()
.setWorkerFactory(DIWorkerFactory(parentComponent))
.build()
WorkManager.initialize(context, configuration)
Run Code Online (Sandbox Code Playgroud)
使用工人
val request = OneTimeWorkRequest.Builder(workerClass).build(HardWorker::class.java)
WorkManager.getInstance().enqueue(request)
Run Code Online (Sandbox Code Playgroud)观看此演讲以了解有关WorkManager功能的更多信息。
| 归档时间: |
|
| 查看次数: |
3991 次 |
| 最近记录: |