So from what I read, Dagger doesn't have support for inject in Worker yet. But there are some workarounds as people suggest. I have tried to do it a number of ways following examples online but none of them work for me.
When I don't try to inject anything into the Worker class, the code works fine, only that I can't do what I want because I need access to some DAOs and Services. If I use @Inject on those dependencies, the dependencies are either null or the worker never starts i.e the debugger doesn't even enter the Worker class.
For eg I tried doing this:
@Component(modules = {Module.class})
public interface Component{
void inject(MyWorker myWorker);
}
@Module
public class Module{
@Provides
public MyRepository getMyRepo(){
return new myRepository();
}
}
And in my worker
@Inject
MyRepository myRepo;
public MyWorker() {
DaggerAppComponent.builder().build().inject(this);
}
But then the execution never reaches the worker. If I remove the constructor, the myRepo dependency remains null.
I tried doing many other things but none work. Is there even a way to do this? Thanks!!
You need to look at WorkerFactory, available from 1.0.0-alpha09
onwards.
Previous workarounds relied on being able to create a Worker
using the default 0-arg constructor, but as of 1.0.0-alpha10
that is no longer an option.
Let's say that you have a Worker
subclass called DataClearingWorker
, and that this class needs a Foo
from your Dagger graph.
class DataClearingWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
lateinit var foo: Foo
override fun doWork(): Result {
foo.doStuff()
return Result.SUCCESS
}
}
Now, you can't just instantiate one of those DataClearingWorker
instances directly. So you need to define a WorkerFactory
subclass that can create one of them for you; and not just create one, but also set your Foo
field too.
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
}
}
Finally, you need to create a DaggerWorkerFactory
which has access to the Foo
. You can do this in the normal Dagger way.
@Provides
@Singleton
fun workerFactory(foo: Foo): WorkerFactory {
return DaggerWorkerFactory(foo)
}
You'll also need to disable the default WorkManager
initialization (which happens automatically) and initialize it manually.
In the AndroidManifest.xml
, you can disable it like this:
<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" />
Be sure to replace com.your.app.package with your actual app's package. The <provider
block above goes inside your <application
tag.. so it's a sibling of your Activities
, Services
etc...
In your Application
subclass, (or somewhere else if you prefer), you can manually initialize WorkManager
.
@Inject
lateinit var workerFactory: WorkerFactory
private fun configureWorkManager() {
val config = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
WorkManager.initialize(this, config)
}