Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger2: Unable to inject dependencies in WorkManager

Tags:

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

like image 714
varunkr Avatar asked Sep 20 '18 22:09

varunkr


People also ask

How do you implement a WorkManager?

Stay organized with collections Save and categorize content based on your preferences. To get started using WorkManager, first import the library into your Android project. Once you've added the dependencies and synchronized your Gradle project, the next step is to define some work to run.

Is WorkManager a service in Android?

Android WorkManager is a background processing library which is used to execute background tasks which should run in a guaranteed way but not necessarily immediately. With WorkManager we can enqueue our background processing even when the app is not running and the device is rebooted for some reason.

Which existing policy is available in WorkManager?

WorkManager PoliciesKEEP — keeps the existing unfinished WorkRequest. Enqueues it if one does not already exist. REPLACE — always replace the WorkRequest. Cancels and deletes the old one, if it exists.

When would you use a WorkManager?

Use WorkManager for reliable workWorkManager is intended for work that is required to run reliably even if the user navigates off a screen, the app exits, or the device restarts. For example: Sending logs or analytics to backend services. Periodically syncing application data with a server.


1 Answers

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) } 
like image 180
Craig Russell Avatar answered Sep 17 '22 12:09

Craig Russell