Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
540 views
in Technique[技术] by (71.8m points)

android - Dagger2: Unable to inject dependencies in WorkManager

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!!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Overview

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.

Example

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)
}

Disabling Default WorkManager Initialization

You'll also need to disable the default WorkManager initialization (which happens automatically) and initialize it manually.

How you do this depends on the version of androidx.work that you're using:

2.6.0 and onwards:

In AndroidManifest.xml, add:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="YOUR_APP_PACKAGE.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>

Pre 2.6.0:

In AndroidManifest.xml, add:

 <provider
        android:name="androidx.work.impl.WorkManagerInitializer"
        android:authorities="YOUR_APP_PACKAGE.workmanager-init"
        android:enabled="false"
        android:exported="false"
        tools:replace="android:authorities" />

Be sure to replace 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)
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...