Dagger for Android: A Detailed Guide

Tompee Balauag
Familiar Android
Published in
6 min readDec 5, 2018
Photo by Kwang Mathurosemontri on Unsplash

In this article, we will be exploring Dagger2 for Android. For context and refresher on the “Java” way, have a look at my previous article about Dagger 2 basics here and scopes and subcomponents here.

Two of the main reasons why we should use the Dagger version for Android is because it allows you to write less verbose code and it upholds the DI principle that a class should not know anything about how or what it is being injected with. Now what do we mean by that? Consider this example.

This is a common practice when building subcomponents and injecting. This code is usually placed in an activity or any top-level android framework type because field injection is the way to go for objects instantiated by the framework. The main issue here is that, this class builds its own container so it knows about its injector. Inversion of control states that it should not build its dependencies but rather, some class outside of it.

Dagger for Android addresses both of these concerns by introducing android framework type injectors and additional annotations. Before we start, lets add dagger for android in our dependencies. I’m using Kotlin so I will be using kapt instead of the annotationProcessor.

implementation "com.google.dagger:dagger:$version"
implementation "com.google.dagger:dagger-android:$version"
implementation "com.google.dagger:dagger-android-support:$version"
kapt "com.google.dagger:dagger-compiler:$version"
kapt "com.google.dagger:dagger-android-processor:$version"
kapt "com.google.dagger:dagger-android-support:$version"

Building our Component

Component building is same as before, with some added modules. Let’s work our way through an example.

@Singleton marks the scope of this component. @Component is used to declare that this interface is a component and dagger should implement it. Notice that in the modules, we added AndroidSupportInjectionModule. This will allow us to use the android framework type bindings (Activity, Fragment, Service, etc). This module extends the framework type bindings to support library types. If you do not intend to use the support library, you can use the AndroidInjectionModule instead. We now add a custom component builder to bind the application instance to the container using @BindsInstance. Note that binding the application instance via module also works but is more tedious and more verbose. Finally, we define a method to inject our application instance. This is the only thing we need to define in our activity really, aside from extra application-wide modules needed.

Injecting the Application

Before injecting the application, we need to implement some interfaces first.

Notice that we need to implement new interfaces such as HasActivityInjector, HasServiceInjector and HasFragmentInjector. These declares that our application can inject activities, services and fragments. We will see how later. You therefore need to implement the get injector methods. These injectors can be acquired from the component because our component includes the AndroidSupportInjectionModule. This method (implementing interfaces) is the best way to customize the injection scope of your application. Sometimes, you don’t need to provide an injector for fragments in application because you want your activity to do that. Or you don’t have any services in your application which leaves you with activity injector only. Therefore, you can implement HasActivityInjector only.

If you don’t care about all these intricacies, there is a less verbose way wherein you can do all those stuff. You can basically inherit DaggerApplication. DaggerApplication implements the following, HasActivityInjector,
HasFragmentInjector, HasServiceInjector, HasBroadcastReceiverInjector, HasContentProviderInjector and HasSupportFragmentInjector if using support library so you don’t have to.

Your application will only contain this amount of code. Your component should also be updated (should now inherit from AndroidInjector).

The inject method is also removed because AndroidInjector already defines it.

Now let’s proceed to activity injection.

Activity Injection

Activity injection can be configured in 2 ways, the first one giving you more control but is a whole new level of tedious to write, the second one being extremely simple but less customization.

Before we check the two approaches, let us define an activity. This activity will be injected with a Warrior instance.

Yes, injection only involves 1 line, AndroidInjection.inject(this). This should be called before super.OnCreate to ensure that injection happens before fragment attachments.

Now let’s look at the tedious method first. This method requires you to write a subcomponent and attach it to the main component. Let’s work our way through an example.

First create a module that will provide your application dependencies. This is common to both approaches.

Now we will create a subcomponent for MainActivity. You will do this for all activities you want to inject.

This subcomponent should contain the scope and the modules required to satisfy the activity’s dependencies. The subcomponent should inherit from AndroidInjector. A custom builder class can be implemented as well.

Now that we have the subcomponent, it’s time to bind it in the application component. To do this, you need to create an activity module. This activity module can be thought of as a module that provides all activity injectors.

This module should include all activity subcomponents you want to bind. For example you have 3 activity subcomponents, list them in the subcomponents and bind them. This module acts as a map of injector factories. We will see its purpose in a moment.

The last step is to include this ActivityModule in the AppComponent.

Let’s try to visualize the relationships of each classes we just created. I also added other details to give you an idea on how to extend for other framework types.

AppComponent includes the ActivityModule which is essentially a mapping of classes to their respective injector factories. The injector factories are essentially your subcomponents that is implemented by your activity module.

This works as follows. When an activity calls AndroidInjection.inject(this), it gets an instance of the DispatchingAndroidInjector<Activity> from the application. This injector, provided by the AndroidSupportInjectionModule, looks for the appropriate injector factory for your activity (if none is defined and mapped, a compile error is encountered), builds it (essentially your subcomponent) and passes your activity for injection.

With this, all the intricacies of dependency configuration now lays outside the scope of the activity. All your activity has to do now is to call inject and assume the best.

Now onto the less verbose approach. Be warned that this approach is only applicable if your subcomponents does not require any custom builder (which is most of the time true).

First we create the same module to satisfy our activity’s dependency.

Now, instead of creating a subcomponent, we use a different annotation in our ActivityModule, the @ContributesAndroidInjector.

This annotation automatically creates a subcomponent for you. Just provide the module implementations and scopes and you’re good to go. The thing to note here is that the binding function should return the same type as the activity you want to be injected.

One of the not-so-obvious implication of this approach is that since you passed your activity in inject, your component (or subcomponent) now has an instance of your activity and you can therefore use your activity context inside of your MainActivityModule. This approach is helpful when you want an instance of your fragmentManager in your module for example.

Fragment Injection

Fragment injection is similar with activity injection. However, if you are using support library fragment, you use AndroidSupportInjection.inject(this) instead. Injection is done preferably in onAttach.

The configuration is pretty much the same. You can apply both approaches. Just make sure that you have an instance of the DispatchingAndroidInjector<Fragment> anywhere in your application or host activity.

You can also localize your FragmentModule inside your host activity module if you don’t want to declare them in your AppComponent directly. Take a look at this sample code.

Assuming you have a MainFragmentModule that satisfies your fragment’s dependencies, you can use @ContributesAndroidInjector inside your activity module. Note that binding functions must always be an abstract class or an interface. To workaround this, we can use the includes method of module.

That concludes our Dagger 2 for Android tutorial. The source code for the ContributesAndroidInjector approach can be found here. The subcomponent approach can be found here.

--

--