I’m sure that the majority of you reading this blog already have some kind of idea about Dagger, especially if you are an Android developer because it turns out that a lot of companies require or expect you to know something about Dagger, and there is a reason for that. While Dagger can be very complex, it is also an awesome tool to work with.
Many times in Android development we rely on objects that need to be initialized. For instance, let’s say that you have a simple screen in order to display a list of movies, so on Android you will probably have a fragment which lives inside of activity and that fragment have some kind of Web API client which is probably powered by Retrofit or some kind of repository in order to get and cache movies depending on the project requirement. You probably already figured out that, those are dependencies, for instance, the fragment in the activity, the recyclerView adapter, the Web API client or the repository that will live in the fragment needs to be provided, and the most straightforward way is to just let each object obtains their own dependencies references.
Dependency injection is a design principle, where you are actually giving the responsibility of providing object dependencies to a third party via either field, constructor or method. Does that sound cool to you? Well, maybe not, maybe yes, or at least not yet, right? ;).
Let’s talk about the benefits of using DI and I’m pretty sure that after this you will find it a bit cooler. :D
More reusable code — dependencies that are injected can be configured externally, which increases the reusability of that component.
More readable code — it is easier to spot what an object depends on.
More testable code — dependencies can be configured externally to provide a different implementation or a Mock, which increase the testability of your code.
Promote abstractions instead of concretions — it is a good practice to provide abstractions instead of concretions with your dependencies. Although it is possible to provide concretions, DI promotes the use of abstractions and that is a very good thing.
Dagger 2 is a very popular dependency injection framework on Android, especially when using Java. It is based on the Java Specification Request (JSR) 330.
Unlike other DI framework, Dagger 2 works with annotations processors to generate a very easy to read code, which makes it great in terms of performance but also very convenient for developers, because if you want to, you can actually see the magic behind it by going through the full traceable code it generated (don’t worry most people do not even bother with that code, but it is a good thing that it is there, just in case :D).
Now that we have a better overview of Dagger, let’s create a simple movie android application, that will fetch a list of popular movies from The Movie Database API using Dagger 2, easy right? :D
Dagger 2 introduce Scoping, which is a mechanism of reusing and retaining an object instance during the whole component lifecycle.
So scoping, in other words, would mean to tell Dagger 2 to create a local singleton inside a specific component.
With @Singleton scope, Dagger 2 will make sure that any dependency using this scope will be created only once within the same component.
@Singleton, will behave exactly as any custom scope, but since it is provided for us by default and use the name Singleton, it makes more sense to use it for dependencies that will live within the Application level, obviously right? :D
Note that, if you do not scope your dependency, Dagger 2 will always provide a new instance each time it is requested within a component.
First let’s create 2 scopes for our Movies application, one for our Fragment lifecycle and one for our Activity lifecycle.
Components and Subcomponents
A component in Dagger 2 is a user-defined object which is responsible to glue everything together, it uses provided modules in order to create and inject dependencies.
A Subcomponent is a component which lives inside another component, like a child component, but can only have one parent and share it’s parent dependencies. However, a parent component can have many subcomponents but do not have access to their subcomponent dependencies.
One of the reasons why you will want to create a subcomponent is because of Scope. Let’s say that you have an application component with @Singleton as scope and you create a new Activity component which is a subcomponent of the top level parent component. You do not really want for dependencies that should live only as long as the activity is alive to share the same @Singleton scope as the parent component. But instead, you want to provide a different scope for that activity so that, as soon as the activity dies, so it’s dependencies. You can save a lot of resources this way :), so it is a better approach for this example to just provide a subcomponent for each of your activities with a different scope, for instance, the @ActivityScoped created above. However, the activity would have access to the parent component, therefore it’s dependencies :).
Let’s create our custom Application that should extend DaggerApplication.
Now let’s create our application component.
AppComponent scope is @Singleton and we use the annotation @Componenent to tell Dagger 2 that, this interface is a Component.
That’s it, right? Naaaah, a component is really useless without a Module, because it will not know how to get an instance in order to inject them since a Module is responsible for telling Dagger how to provide dependencies from the dependency graph.
Let’s now create an ApplicationModule which will provide our project singleton instances, like Retrofit, Picasso…
@Provides and @Binds are used to register dependencies for injection.
Notice that I used static methods where @Provides are used. Static methods are fast and this way may also be easier for the compiler, so it is a good practice to use a static method with @Provides.
@Binds is very similar to @Provides but it is used when you want to bind an implementation. For example, in the last method, we will bind the Context to the Application so that any object requesting the context will have instead the Application injected. It is best to use @Binds as much as possible since it is much more efficient because it reduces the line of codes created and the object creation (it will generate about 40% less code than @Provides).
While using @Binds, if an object requires some dependencies (field or constructor injection), Dagger 2 will be smart enough to automagically provide those dependencies, if they are available in the component :).
Note that: @Binds methods must have only one parameter whose type is assignable to the return type
Picasso required the Context and since we provide Context already (on bindContext ), Dagger 2 will automagically provide that context to the providePicasso method.
But what about Application on bindContext method, how Dagger will know about it? Well, for now, it doesn’t because we haven’t provided it yet. Fortunately for us, Dagger comes with something called @BindInstace. Sometimes you can end up with an object that is available only at the time you are actually building the component, for instance, the Android Application and by adding @BindInstance to the component builder, will allow that instance to be injected inside the component.
Now we have to update our AppComponent and add @BindInstance in order to inject our Application inside the component.
Our new AppComponent will look like this now
Now Dagger is aware of the Application and will be able to provide it when it is needed.
Let’s now create our Activity Module :), in order to provide the Fragment, Presenter, RecyclerView Adapter, the DataSource to get movies from theMovieDB server…
Dagger 2.11 introduce ContributesAndroidInjector, which frees us from creating a bunch of boilerplate code in order to create a Fragment Subcomponent or an Activity Subcomponent with their respective binding…, now all you have to do is to use the @ContributesAndroidInjector annotation and make your fragment to extend DaggerFragment or DaggerActivity in case of an Activity. We will see how to create an activity component below.
We will create just for the sake of separating concerns, a new appComponent Module that will be responsible for providing Activities subcomponents only.
That’s it :), same as fragment @ContributesAndroidInjector will do the trick, and we pass our activity module MainModule.class.
Now we need to update our AppComponent with all those modules in order for Dagger to know about the whole dependencies graph.
We had to add the provided AndroidSupportInjectionModule(or AndroidInjectionModule if you are not using Android support library) since we are using DaggerApplication and @ContributesAndroidInjector.
Now we need to init our AppComponent in our MoviesApplication and our updated file will look like this.