Dagger.android — The Missing Deep Dive

Lisa Watkins
Code With Lisa
Published in
4 min readMar 9, 2019

I know what you’re thinking — not another Dagger article. There are a ton of great resources out there that explain the basics of dependency injection with Dagger. This article is not one of those. I’m going to assume that you are familiar with Dagger, and I’m also going to assume that you’ve probably used Dagger in one of your applications.

The fundamental problem with Dagger and Android applications is that without Dagger.android , any Android component that you want to inject has to know about how it is injected. We get a lot of code like this:

Yuck, this definitely violates a major principle of dependency injection: a class should never know about how it is injected.

This is why I want to specifically talk about Dagger.android and why, after working with Dagger in my projects for over a year, I feel as though I’ve finally seen the light. This is how Dagger is supposed to look and feel with your Android projects.

First things first, lets define our ApplicationComponent and ApplicationModule.

Notice that the first module included in ApplicationComponent is AndroidInjectionModule. This module provides the bindings for all Android framework base types (activities, fragments, services, and broadcast receivers).

In ActivityModule and FragmentModule, notice the annotation @ContributesAndroidInjector. This annotation is purely syntactic sugar to bind an AndroidInjector with the activity or fragment. When you look at the generated code for ActivityModule_ContributesMainActivity, you’ll see a generated map binding with the activity name as the key and a subcomponent that extends AndroidInjector .

Generated code below:

When we look at more generated code later, it will become more clear how Dagger is building AndroidInjectors to inject our activities and fragments.

Now, the real bread and butter of using Dagger.android — our application does not need to know anything about how it is injected. The work can be deferred to ApplicationInjector.

Moving on to our Application class…

As you can see, we’ve implemented HasActivityInjector and we’ve overridden activityInjector() to return an injected instance of DispatchingAndroidInjector<Activity> . When ApplicationInjector builds the application component, you can see that in the generated code we land here…

Generated code below:

We’re setting up MainActivity's subcomponent builder. When ApplicationInjector moves on to inject BaseApplication, we land here…

Generated code below:

When we inject BaseApplcation, we also inject its member variables. See getDispatchingAndroidInjectorOfActivity? This is exactly how we inject BaseApplication's dispatchingAndroidInjector with the proper map. So far we just have one entry, MainActivity and its subcomponent. Now every time we inject an activity in our application, we check for it in dispatchingAndroidInjector to complete the injection.

Now it is clear that @ContributesAndroidComponent generates a map of our activity classes to their subcomponent builders, which each in turn generate a map of all their child fragments and their subcomponent builders.

Now, lets take a look at MainActivity.

Just how our Application class impelmented HasActivityInjector , our MainActivity class implements HasSupportFragmentInjector. Same exact deal here. Our DispatchingAndroidInjector<Fragment> member variable is injected with the mapping of our fragments to their subcomponent builders.

Now, all we have to do to inject our activities and fragments is call AndroidInjection.inject(activity) and AndroidSupportInjection.inject(fragment) . Lets check out what AndroidInjection.inject(activity) is doing.

dagger.android.AndroidInjection

We first make sure that our Application class is an instance of HasActivityInjector — then we call .activityInjector(). Remember we implemented that method in BaseApplication? Here we return the DispatchingAndroidInjector<Activity> instance that has a list of AndroidInjector factories. If we can’t find the mapping, we’ll throw an exception. If all is well and we do find the mapping, we’ll call inject() and our subcomponents factory will handle all member variable injection for our Android component.

A really nifty way to handle AndroidInjection.inject is to register it in lifecycle callbacks for your application and have your injectable fragments implement an empty interface. I called my empty interface Injectable. Check out an updated version of ApplicationInjector below:

Now, any time we create a fragment, if it implements Injectable we take care of the injection.

I hope this article conveys how excited I am to be using Dagger.android in my projects, and why you should too. Hopefully this article also demystified how things are working underneath the hood, and its clear how you can use Dagger.android to inject all your Android components moving forward.

Thanks for reading, and happy coding!

--

--

Lisa Watkins
Code With Lisa

Engineer, Activist, Cat Lady. Mobile engineering @ Lyft.