Dagger 2 Generated Code.

Why an another Dagger 2 article ?

I know what you are thinking. Why another Dagger 2 article.. right?? But the purpose of this article is not to show you how to use Dagger 2 on your project(if you are looking for one, here is a good example) but to take a deep dive into Dagger’s generated code and understand what Dagger does internally to generate your dependency graph.

When I started using Dagger, I used it like a black box for few months. I knew how to use it, but I had no idea how it internally generates my dependency graph. So, whenever I got an error, it took a lot of time to fix. So one day I decided to try to read the generated code and that’s when I realised, it is not as difficult as its reputation.

Prerequisites:

  1. Builder Design Pattern
  2. Factory Design Pattern

If you are not familiar with these two design patterns, you might find rest of the article a little confusing. So if you need a little refresher, there are plenty of resources online.

3. Familiarity with Dagger 2 Library

As mentioned before, this article is not an introduction to Dagger 2. However if you have never worked with Dagger 2 or need a refresher, refer to the below article by Janishar Ali.

Example:

We are going to use a fork of MindorksOpenSource’s android-dagger2-example for code sample.

Here is a tree representation of the dependency graph for our example repo.

Graph Generated by daggraph

To generate this dependency we need two modules(ApplicationModule & Activity Module) and their respective components(ApplicationComponent, ActivityComponent).

ApplicationModule.java:

ActivityModule.java:

ApplicationComponent.java:

ActivityComponent.java:

Now we can inject provided dependencies anywhere in our App. For example if we want a DataManager instance in our application class for some reason, we can easily inject it like this.

DemoApplication.java

If we want the same thing in an Activity we can get it like this.

Let’s see how constructor of DataManager class looks like.

DataManager.java:

We can see that even though DataManager class has dependency on three external classes Context ,DbHelper and SharedPrefHelper , we can easily inject it with just one line. This is how Dagger 2 makes your life easier.

Till now we have discussed the part we all know. Time to go a little deep and see what Dagger 2 did under the hood.

Generated Code:

First, we are going discuss each generated class separately to understand their responsibilities. Once we do that, we are going to discuss how all these classes work together to make our life so easy.

Basically, Dagger 2 generates a bunch of factories for your provided dependencies. If you remember, we have two @provide annotations inside ActivityModule and five inside ApplicationModule . And that’s why Dagger generated seven factories inside ---.android_dagger2_example.di.module package.

PS: If you are using Java you can find these generated files inside app/build/generated/source/apt , and if you are using Kotlin you can find the same inside app/build/generated/source/kapt package. I have added them to my git repository in case you don’t want to build the project.

If you look at our ActivityComponent class, it has dependency on ApplicationComponent (fig right), and we are exposing Application , DataManager,SharedPrefsHelper , DbHelper to ActivityComponent from ApplicationComponent .(fig left)

To provide these dependencies, three factories are generated inside com.mindorks.example.android_dagger2_example.data package. If you are thinking why only three factories? It’s because factory to provide Application is already generated before, no need to generate it again.

Now let’s see what those factories look like.

All factories implement an interface Factory<T> (part of dagger.internal package ) which further extended from an another interface Provider<T> (from javax.inject package). Provider<T> interface looks like this.

public interface Provider<T> { 
T get();
}

That means all of our factories overrides this method. Let’s take ApplicationModule_ProvideDatabaseNameFactory as an example.

From the generated file above, we can see the implemented get() method returns databaseName with the help of ApplicationModule instance. Also there is an another method proxyProvideDatabaseName which does the *same thing as get(). We will talk about this method later. Before that let’s look at the DaggerApplicationComponent class.

As you can see, this class uses Builder Design Pattern, where you provide an ApplicationModule instance. When you build it, it basically initialises all the factories using this instance(refer to initialize() method). Since DaggerApplicationComponent implements ApplicationComponent interface, it overrides all the methods (inject(), getApplication(), getDataManager(), getPreferenceHelper(),getDbHelper() ) of ApplicationComponent. And if you look at these methods, they are just calling either get() or proxyProvideXXX() methods of their corresponding factories, which we have discussed previously.

Let’s discuss the last part of the generated code, Injectors. Since we are looking at ApplicationComponent, we will discuss it’s Member Injector.

This class is generated for injecting members. As you can see, this class implements below interface, which has one method injectMembers(T) .

public interface MembersInjector<T> {
void injectMembers(T instance);
}

So DemoApplication_MembersInjector overrides this function. It takes Application class as an argument and initialises injected members using those previously generated factories. In our example, we are injecting DataManager to DemoApplication class, so it initialises the dataManager variable using datamanagerProvider factory.

Connect the cycle:

We have discussed all parts of generated code in our example. Let’s connect those and see how the full cycle of Dagger 2 injection works.

Suppose our need is to inject a DataManager instance in our application.

Step1: We create anApplicationModule and an ApplicationComponent . If you build the project at this stage Dagger will generate all the factories and DaggerApplicationComponent .

Step2: Build DaggerApplicationComponent instance.

appComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();

When we build the instance we pass an instance of ApplicationModule to DaggerApplicationComponent . This instance is used to initialise all the factories. (refer to initialize() method )

Step3: Inject DataManager instance

@Inject
DataManager dataManager;

After adding above lines to your Application class, if you build the project, Dagger 2 will generate MemberInjector class for your Application. It takes the instance of your application class and initialises its members with the help of provider factory. In our example which is DemoApplication_MembersInjector class.

Step4: Call inject method of DaggerApplicationComponent which actually injects an instance of DataManager to our variable.

applicationComponent.inject(this);

the overridden inject() method calls MemberInjector’s injectDataManager method with an application instance and a provider instance. This method initialise the dataManager variable of our application to an instance of DataManager class.

This is what Dagger 2 does internally to provide our dependencies.

Conclusion:

I feel the best way to understand Dagger 2 is reading its generated code. So if you feel getting lost at any time. Just try to read the generated code and may be it will help you figure out your problem.

If you liked this article please click the 👏 below. I would love to hear your thoughts in the comment section or at Twitter. Thanks a lot !