How to pass custom parameters to RxWorker using Dagger 2
By: Udayaditya Barua
Tags: Rxandroid, Android, Technology, Kotlin, Tutorial

Say Hello To WorkerFactory

What is a WorkerFactory?

A factory object that creates ListenableWorker instances. The factory is invoked every time a work runs

In other words, if your worker factory class is registered to WorkManager, then it’ll get called each time to create an instance of the worker class which we discussed above. That means, now you have control over the constructor parameters!

I am here to guide you weave all the components together to get a hassle free worker class and dagger implementation

Step By Step Implementation

First, we’ll start by creating an Annotation:

Why is this necessary?

Since we’ll be using Dagger and WorkerFactory, we need a way to give the factory the data to create instances of our worker(s)

Here we create a Map of .class vs instances of the workers we have in our code base using this annotation. We then provide this map to our WorkerFactory class to iterate and use the concrete instance from the map.

The map will look something like this:

Map<Class<? extends RxWorker>, Provider<RxWorker>> getWorkerMap();

Provider is of type java.inject used by Dagger to get instances. Don’t worry about it too much. Just remember this piece of code.

Create a Binding Module — Key to the value

Ok, now we need to use the annotation somewhere. If you remember, this sort of implementation is done when designing the framework for injecting ViewModels in our activities. Similarly here, we need to create a module to bind the key to the value.

Simple enough? SampleWorker.class on line 6 is the key, RxWorker on line 7 is the value for the Map . Dagger then tries to create instances of the value objects.

Add the custom parameter to the worker class we want and annotate @Inject

Coming back to our worker class, say we want to inject Retrofit in our worker class. We add the parameter and annotate the constructor with @Inject like dagger wants us to do.

Now, when dagger tries to create this map and fill the values with the instances, it’ll invoke our custom constructor of the worker.

But there’s an important step missing here, how will it inject Retrofit? Hold that thought! We’ll come back to this later.

So, what do we have so far? We have:

Annotation defining a key for Class vs Instance map

Binder module which specifies the map dagger needs to create

Our custom worker class which will actually perform our background task

We need to tell dagger to attach this module to its graph so that it can start injecting, right? You may want to hit build and see whats going on till now.

The thing is, it throws an error complaining that it cannot inject Retrofit. Specifically Retrofit cannot be injected without @Provides annotation .

Now for dagger to inject Retrofit, it needs the instance of Retrofit from somewhere right. I am going out on a limb here and assuming you have a Repository module with you which has all the @Provides for Retrofit , OkHttp and works. If you don't, best you create one.

Remember the WorkerBinding module above? Include the Repository Module here:

@Module(includes = RepositoryModule.class)

public abstract class WorkerBindingModule { ...

The only thing left is adding this module to the parent component so that dagger recognises it. Go ahead and do that.

Add the WorkerBinding module in AppComponent

Sounds simple right? So go ahead and add this module in the list of modules in the app component and hit build .

At this point, your build will fail and dagger will spew out a whole lot of errors. Most prominently, it’ll say cannot bind Retrofit in 2 instances and it'll print the entire graph.

So what does this mean?

You will not get this error if you haven’t used repository pattern and not included retrofit anywhere else.

This error is basically thrown because:

We have created a binding (the map which we talked earlier) for the ViewModels also! Which in turn include Retrofit.

We have included WorkerBindingModule in the top-level i.e., the app component level.

The graph has become something like this:

Rough sketch of the dagger graph

As shown, on one side, Retrofit is getting injected in the level SubComponent, but here, we try to directly inject it. Dagger doesn't like this. More so, it'll not be able to figure which class needs which particular instance of Retrofit .

Ok, so you probably guessed what we need to do now. Remove that line where we have included the worker binding module in the AppComponent and hit build.

I just wanted to show this error so that you understand what is going on here.

Now dagger will not complain. But now, our module is out of the graph, so it will not do anything i.e., there will no injection happening. We still need to integrate it into the graph.

Enter SubComponent — Create a worker SubComponent

If you notice, we didn’t include Context here. Why, because of the same problem, it's injected on a different level. Context is provided at the AppComponent level and not in sub component level. Don’t worry, you’ll get the context. Wait on.

In order to create this Map , we have included our WorkerBindingModule here as well. This provides dagger with the necessary requirements for creating this map.

So when dagger starts to populate the map (as you will see where), it’ll call our Worker constructor with the extra Retrofit parameter.

Create the WorkerFactory Class

Comes the part where we guide android how to create our worker class using our custom factory.