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
Notice that the first module included in
AndroidInjectionModule. This module provides the bindings for all Android framework base types (activities, fragments, services, and broadcast receivers).
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
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
Moving on to our
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
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
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
AndroidSupportInjection.inject(fragment) . Lets check out what
AndroidInjection.inject(activity) is doing.
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
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!