1 - What is Dependency Injection?
Dependency Injection is a design pattern which help us to reduce the coupling between modules (classes). Hence, that makes our modules reusable, interchangeable and easy to test.
For example, in our Android app (more specifically, in LoginActivity), we must hit the authentication endpoint via AuthenticationApi to verify if user information is correct.
1.1 - Not using DI
This is the AuthenticationApi class, which will contact the server and verify input credentials:
Then, inside LoginActivity, we instantiate new AuthenticationApi in onCreate() method.
This cause a strong couple between LoginActivity and AuthenticationApi which is not really good. Like:
- What if we want to add a Logger in IAuthenticationApi when running debug but not release version?
- What if we want IAuthenticationApi return mock data when running test?
- What if in demo version, we want IAuthenticationApi retrieves data from local database instead of server?
This is where Dependency Injection jump into the picture!
1.2 - Using DI
In Dependency Injection pattern, we usually have a Factory class which provides all the dependencies for the module/application.
Then, inside Application class, we create a new instance of ApiFactory and expose public method to refer to. By this way, we can access the ApiFactory throughough entire application:
Inside LoginActivity, we can easily refer to ApiFactory to get the IAuthenticationApi object:
As you can see, by using DI, now the LoginActivity does not depend on the AuthenticationApi anymore! Basically, LoginActivity doesn’t know the concrete implementation of IAuthenticationApi, the type of IAuthenticationApi was decided by the ApiFactory.
By having multiple ApiFactory based on build type, you can easily decide the concrete implementation of IAuthenticationApi in each case.
1.3 - Conclusion
After 2 examples above, DI is quite simple, right?! But if DI is simple, why do we need a framework?
Basically, the technique of DI is simple. But if your project is quite big, you have so many modules depend on each others, the dependencies will become more complicated. And it also produce a lot of boilerplate code which is not so beautiful!
2 - Dagger2 Introduction
2.1 - What is Dagger2?
Dagger2 is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier version created by Square and now maintained by Google.
2.2 - Why using Dagger2?
Traceability
- Easy to navigate around the entire project using Find Usages… and Open Declaration
- Trace through the code that clear and well structured
Clarify API
- Simpler @Module declarations
- Injections will fail at runtime if not correctly configured, which helps you find mistakes faster
Performance
- No performance hit because injections are not performed using relfection like other dependency injection frameworks
3 - How to use Dagger2?
3.1 - Set Up
Add following plugin and dependencies into app scope build.gradle file:
3.2 - Define Module class
Module classes is where you define all the dependencies provider for your application. You can have many Module classes in your project.
For example, below is the Android Module class which provides all the main Android objects so you can easily inject later:
Or another example below - ApiModule, which provides all Api related object for the application:
When you declare a @Singleton annotation, Dagger2 will manage all the concurrency and make sure return the same object everytime for you. You don’t need to care about that anymore! Cool huh?
3.3 - Declare Components
Component interfaces is where you declare the association between Module and the Injection Target. In other words, the Component interfaces told Dagger2 that dependencies in specific Module will be used in listed classes.
By the way, in case you didn’t notice, the DaggerMyComponent was auto generated by Dagger2 after you build the project. And all the method like androidModule() was also auto generated based on your Module class name.
3.4 - Instantiate the Component in Application class
Next, inside your application class, instantiate the Component you just declare above:
3.5 - Injection
Now you can inject the dependency that you need inside any classes you declared in the Component by simply using the annotation @Inject. And before using any Injected instance, remember to call the inject() method of Component:
Sample code:
4 - Instrumentation Testing with Dagger, Mockito, and Espresso
In instrumentation test, we usually mock the server response to make sure the test suite can cover every cases from succeed to fail.
In this part of the post, I’ll show you how to write instrumentation tests with Dagger, Mockito and Espresso.
Beside the official ApiModule which will be applied for release build, we also need a DebugApiModule for the debug build. This module will decide to return the real or mock object base on the mock mode:
Under the debug code base directory, we create another MyComponent class:
You can notice that, instead of using ApiModule we will you DebugApiModule this time.
Then, we modify the Application class to expose a method to set the mock mode:
Now, in test class (below is the code of BaseActivityTest which will be extended later), we can easily switching between mock and real mode:
5 - Sample Code
So, that’s all for Dagger2 in this post.
You can find the full sample source code on my Github page.
In later posts, I’ll cover some advance topics of Dagger2.
Hope you enjoy it! :D