Writing Testable Code for Android Apps Using Dagger2

Ismail Khan
7 min readJun 18, 2018

--

Welcome back! If you are here and reading this article then I hope you have read the previous one and are curious to know how to write testable code using Dagger2. We will make use of the same example where an app fetches a String from the server and stores in a cache for later reference.

Ready, Set, Go!

One of the advantages of using Dagger2 that was pointed out in the previous article was that we can reduce the boilerplate code while creating our dependencies(there are many more advantages besides this). Specifically like below code which we need to write every time we need an instance of the Repository.java class

What if I say we could replace the entire code above with just a single line like below in our MainActivity.java class ?.

@Inject Repository mRepository;

Sounds interesting right ?! yes of course it does, but in order for that to happen we need to build it, so that we can enjoy it. Its like building an office building and enjoying its comfort. The more effort we put in building the office smartly the more we can enjoy it once it is completed.

Dagger2 is based on the Java Specification Request (JSR) 330, which means that there are some standard set of annotation defined which we could use.

But how does Dagger2 know how to create an instance of an object and how to inject it in the MainActivity.java using just the @Inject annotation?.

The answer is — we need to tell Dagger2 how to do it :). We will make use of more annotations to tell Dagger2 how to. Below are some annotations to get started with.

  1. @Provides annotation is used on a method and is used to tell Dagger2 that this method’s return type is a dependency which it can use to fulfil the matching @Inject request.
  2. Methods annotated with @Provides must belong to a class which is annotated by @Module annotation. This class is basically a collection of dependencies (i.e collection of @Provides methods).

In our example app, as we would require a Repository class instance in our MainActivity so we want Dagger2 to handle the creation of Repository instance. By having exposed to the above 2 annotations we can create a class annotated with @Module annotation. This class will consist a method whose return type will be of Repository and also annotated with @Provides annotation. Below is that class.

Note: Look at the naming convention. A class which is annotated with @Module annotation is usually suffixed by Module (RepositoryModule) and the method annotated with @Provides annotation is prefixed by the owrd provides (providesRepository).

From our previous article we know that our Repository class is a Singleton and it accepts two constructor parameters, ApiService.java class and MyDatabase.java class.

So now we have two things to take care of.

  • Providing constructor parameters (ApiService & MyDatabase)
  • Making the Repository instance as Singleton and

Lets take it one by one.

First the constructor parameters — Since Repository class is dependent on ApiService and MyDatabase class. We need to tell Dagger2 how to create the ApiService and MyDatabase instance as well. But before I show you the code I would like to point to you the definition that I introduced to you in the previous article about DI from wiki.

In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state.[1] Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

According to the above definition, since the Repository class is dependent on ApiService and MyDatabase class we don’t create their dependencies inside its class (or providesRepository() method) we instead request it. In our case we will create two different methods which would provide ApiService class instance and MyDatabase class instance. And in the providesRepository() method we add in those two dependencies as parameters.

Note: If a method which is annotated by @Provides has parameters then Dagger looks for other methods annotated with @Provides whose return type matches with that parameter type.

Second is to make Repository instance as Singleton — Making an object Singleton in Dagger is simple, all we need to do is add @Singleton annotation to the method which returns that object type.

Note: In our case since Repository class is Singleton it makes sense to make its dependencies as Singleton as well.

Now, so far we have looked at 4 annotations

  1. @Module — collection of dependencies (collection of @Provides).
  2. @Provides — tells Dagger2 that its return type can be used to satisfy a dependency.
  3. @Singleton — creates a Singleton instance.
  4. @Inject — request for a dependency.

At this point Dagger2 only knows how to create dependencies, it still does not know how to provide those dependencies to classes which request for them (using the @Inject annotation).

Before we go into details how we can bridge the gap between @Module and @Inject, I would like to go back to the analogy that we discussed at the beginning of this article about building an office building. If we were to relate the @Provides annotation in this analogy, then it can be related to different services that we get in our office building like access to PC, internet, food, coffee, printers etc. Now these services can be grouped into different categories for eg food, coffee, snacks can be grouped into, lets say, ‘Pantry’ which in Dagger is @Module annotated class. And the employees (consumers of those services) can be related to MainActivity in our case.

But these services that our office building provides is not available to all, right ?. Only the registered people of that company have access to those services. This is handled by the access card machines, that we use before entering the office. This access card machine is the bridge between the services that the office building provides with its employees.

Similarly in Dagger the bridge between @Module(services)and @Inject(requests) in filled by @Component annotation.

More Analogy…

There is more to @Component in Dagger than just filling the gap between @Modules and @Provides. There is a @SubComponent annotation and also a ApplicationComponent class. So lets understand these different parts.

Lets get the analogy:). Say company A has multiple building. And in each building me have several category of services (@Modules). Lets say that we have building1 which provides service1 and is accessed by person1. Similarly we have building2 which provides service2 and is accessed by person2. If we relate this to Dagger

  • Person1&2 are classes which calls injects — Activities, Fragments etc.
  • Service1&2 are @Modules.
  • Access card machines of building1&2 will be the @Component @Subcomponent.

A little more. Just bare with me :P

But all these information of which person has access to which building must be stored somewhere right, like a server in the main building. In Dagger its called ApplicationComponent class annotated with @Component. It keeps track of all the Subcomponents(buildings). And the subcomponents will have the information of the class/classes which will be using it(person1 &2). It make use of a Map to keep track of which class will use which component(s).

Now lets look at the code. Finally!

ApplicationComponent

  • AndroidInjectModule.class is provided to us by Dagger2. We need not write it ourself. Just understand that it helps with the injection.
  • ActivityBindingModule.class is the class that we create. In this class we store the information of which class uses which component in a Map.

ActivityBindingModule class

Here MainActivity is the key and Subcomponent.Builder is passed as the value. Below is how our Subcomponent looks like.

Subcomponent class

As you can notice this subcomponent make use of RepositoryModule. And this subcomponent is mapped to MainActivity as seen in ActivityBindingModule class above. This ActivityBindingModule is passed to the ApplicationComponent.

What happens is when we call @Inject Repository mRepository; in our MainActivity, Dagger goes to the ApplicationComponent and, in that, inside the ActivityBindingModule it checks if MainActivity is registered with any of the Components or not using the Map which was used in ActivityBindingModule class. If a Component is registered to the Activity it then checks of the methods annotated with @Provides whose return type is that of Repository class. And thats how Dagger knows how to inject dependencies and to which class.

This is how the MainActivity looks.

As you can see we can get the instance of the Repository class by just using the below statement

@Inject Repository mRepositoy;

But there is one extra line in the onCreate method

AndroidInjection.inject(this);

This can also be removed from our MainActivity by putting the above in a BaseActivity and extending the MainActivity with the BaseActivity.

The line AndroidInjection.inject(this) basically calls the ApplicationComponent and asks it to check if the MainActivity is registered or not. The ApplicationComponent is initialised in CustomApplication class of our Android app. Below is the CustomApplication class.

Don’t forget to add this CustomApplication in the Manifest file.

Thats it! thats how different parts of the Dagger2 works. Also there is a lot more to Dagger2 than what was covered in this article. This article was created to help you get started with Dagger2 by giving a good understanding on how different parts work together.

I just realised I forgot to mention the most important part, i.e the gradle dependencies. So here is it

The complete code can be found here.

By understanding how different parts of the Dagger2 works I hope it helps you understand the many resources present online on Dagger2. There is more to Dagger2 than what was covered in this article as I said before. I highly recommend you to watch this below tutorial on Dagger2 by JakeWharton.

--

--