Dagger 2 ? WTF !? — en

I am Terence Zafindratafa, Android developer for one year now. This is my first article, I hope you will like it! I look forward to your feedback :D

— Vous trouverez une version française ici: https://medium.com/@tezharper/dagger-2-wtf-f2e0dfa37ac0

When you try to understand Dagger for the first time…

Dagger 2, a lot of people talk about it, many use it but few understood it from the first tutorial (it’s certainly my case ^^”). The main difficulty encountered in understanding this dependency injection library is the complexity of the proposed examples. Today, I would like to present to you an approach that is as basic as possible for this tool.

To do this, I will start with a quick explanation of dependency injection. Then, we will talk a little bit about the interest of Dagger 2 before moving on to a concrete example.

Many thanks to Philippe BOISNEY for his help in the writing of this article.


Dependency injection, what is it ?

To explain what dependency injection is, I would like to introduce Robert, Android developer, and his newly developed robot Buggy.

Robert is a mysterious guy but maybe a too much solitary one..

The latter has two main features:

  • answer “Like a Monday” when asked “How are you doing?”
  • walk around Robert’s apartment avoiding obstacles.

To develop Buggy, Robert did not use dependency injection.

Result: his robot is quickly functional and executes its functionalities very easily (walking and speaking).

One day, Robert wants to change Buggy’s behaviour because well… having a robot that walks around the house is nice but now he doesn’t do much.

For this reason our developer must put aside his motto “the less I bother, the better”. Indeed, whether it is to change Buggy’s behaviour or test his code, he must touch his code. If its code is sufficiently modular, this should help it considerably. But to modify Buggy’s behavior only during the tests the problem is not solved.

Luckily, Robert finally discovered dependency injection! A revelation for him! Yes, it takes longer to set up, but once done, no more headache! Indeed, Robert divided his robot into several distinct and independent parts (or modules): the head, the right arm, the left arm, etc. These will all be added to Buggy’s body, which, on the other hand, needs these parts to function.

Buggy and his dependencies

These different parts on which Buggy depends are called dependencies. These are created independently of his body, then will be “injected” into his code. That is to say, they are simply “connected” to Buggy’s body, which will use them to walk and talk.

This implies that, if, for example, Robert wants Buggy to take advantage of his ride to clean up, he only has to modify the corresponding dependency (here, one of the arms). In addition, when Robert wants to perform different function tests on Buggy, he will define dependencies especially for this case, and change the robot’s behavior so that it remains motionless during the “walk” functionality test.

“Like a Monday..”
Without injection of dependencies:
advantage: quickly functional
disadvantages: not scalable, difficult to test without touching the production code
With injection of dependencies:
advantages: modular, scalable and easily testable
disadvantage: longer to set up

What about Dagger in all this?

The truth is that there are several dependency injection libraries: Dagger 2 (the most popular) but also Koin, Kodein, etc. However, I am not going to enter into a debate here on the best one to use. I will therefore simply present the advantages and disadvantages of Dagger 2.

If you are a novice, it may be useful to see at least once what it looks like to inject dependencies by hand. You will find examples explained here (java) or there (kotlin).

When this is done, I invite you to continue your reading.

Dagger 2 a.k.a your new laser saber

https://google.github.io/dagger/

Advantages:

  • Generates code for you
  • Very popular: you will find it quite regularly in Android projects
  • You will find a lot of resources for this tool
  • Maintained by Google which has forked the original project created by Square

Disadvantages:

  • Not easy to learn
  • The documentation is not very clear
Dagger 2 will save you a lot of time (once mastered…). Its disadvantages make the library a little complex to handle, but you will see that its advantages overshadow them.

In concrete terms…

Now that you have understood the value of dependency injection and Dagger 2, it is time to move on to practice. It’s in forging that one become a blacksmith, isn’t it? ;)

The DaggerTuto application has two features:

  • display the date and time the application was opened
  • save and display an email address in a TextView.

These are displayed by the MainActivity through two dependencies:

  • SharedPrefsUtils.java
  • DateUtils.java

For a good understanding of how Dagger 2 works, we will only study here the injection of this last dependency. However, you will find a more complete example in the project available on GitHub.

https://github.com/Te-Z/DaggerTuto

01 Dependency

public class DateUtils {
public Date getCurrentDate(){
return new Date();
}
}

The role of the DateUtils.java class is to give a date (here the opening date of the application). This class is actually a dependency of the application (we will use it in MainActivity.java).

The getCurrentDate() method simply returns a new Date() object that will correspond to the opening date of the application.

02 Module

@Module
public class UtilsModule {
@Provides
@Singleton
public DateUtils provideDateUtils(){
return new DateUtils();
}
}

To instantiate the dependency, Dagger will go through a class using the @Module annotation. Inside it, it will be instantiated using a method using the @Provides annotation. The @Singleton annotation ensures that the dependency is instantiated only once.

03 Component

@Singleton
@Component(modules ={StorageModule.class,
UtilsModule.class,
AppModule.class})
public interface TutoComponent {
void inject(DaggerTutoApplication application);
void inject(MainActivity mainActivity);
}

This class allows you to group all the modules together via the @Component annotation. Its role will be to make the link between dependencies and the classes that depend on them. It contains all the modules of the application, including UtilsModule.java previously created.

“inject()” methods, which take dependent classes as arguments, will be used to retrieve dependencies.

Once your component is created, you will need to launch a build of your application. This manipulation will allow Dagger to generate the classes necessary for the injection.

04 Injection

public class DaggerTutoApplication extends Application {
TutoComponent tutoComponent;
@Override
public void onCreate() {
super.onCreate();
tutoComponent = DaggerTutoComponent.builder()
.appModule(new AppModule(this))
.storageModule(new StorageModule())
.utilsModule(new UtilsModule())
.build();
tutoComponent.inject(this);
}
  public TutoComponent getTutoComponent() { return tutoComponent; }
}

To be able to inject our dependencies into the entire application, we create a class which extends the “Application.java” class, the single entry point for any Android application.

From the component, Dagger generated a class with the prefix “Dagger-” (here DaggerTutoComponent). This allows us to instantiate our dependencies via the modules, before injecting everything into the application. You can have more information about this famous class generated here.

“getTutoComponent()” will allow us to use our dependencies in our dependent classes via the component already created (see next part).

05 Let’s use it

public class MainActivity extends AppCompatActivity {
@Inject SharedPrefsUtils prefs;
@Inject DateUtils dateUtils;
(...)
private void configureDI(){
((DaggerTutoApplication)getApplication()).getTutoComponent()
.inject(this);
}
private void setCurrentDate(){
TextView dateTv=(TextView)findViewById(R.id.activity_main_date);
dateTv.setText(dateUtils.getCurrentDate().toString());
}
(...)
}

To use the dependency, we must first inject it via the @Inject annotation AND use the injection method of our component (retrieved from DaggerTutoApplication). This is to let Dagger know that this class will use dependency injection.

You can now use and modify the dateUtils object in your code to modify the application’s behavior, without having to recreate it in your activity.

There you go !

Finally, it only took 5 steps to set up Dagger 2 in our application!

To check if you have understood how it works, I encourage you to look at the project repository to see how the second dependency was set up.

https://github.com/Te-Z/DaggerTuto

The icing on the cake!

Promise it’s almost over ;)

“OK the application is modular, it’s cool. But how do we do these famous unit tests?”

Very good question, dear reader ! Thank you for your passionate interest !

So let’s test the date display by changing the application’s behavior, and thus DateUtils.java. To do this, we will display the date of October 19, 1991 instead of the application’s opening date and use the Robolectric library.

Let’s build on the previous 5 steps:

  • 01 Dependency: create a test dependency inherited from DateUtils, which will modify the getCurrentDate() method to return the date of October 19, 1991.
  • 02 Module: create a test module whose role will be to instantiate our test dependency, via an @Provides method as in the production code.
  • 03 Component: create a test component inherited from TutoComponent that will use our test module. Don’t forget to create an inject() method that will take as argument our future test application class. Once this step is complete, run a unit test so that Android Studio allows Dagger to generate the new classes.
  • 04 Injection: create a test application class inherited from DaggerTutoApplication. We will instantiate our test component there, before injecting it into the application.

Finally, here is the class testing our MainActivity:

@RunWith(RobolectricTestRunner.class)
@Config(application = TestDaggerTutoApplication.class)
public class MainActivityTest {
private MainActivity activity;
@Before
public void setUp() throws Exception {
activity = Robolectric.setupActivity(MainActivity.class);
}
@Test
public void shouldShowProperDate() throws Exception {
TextView date=
(TextView)activity.findViewById(R.id.activity_main_date);
assertEquals("Sat Oct 19 00:00:00 CET 1991", date.getText());
}
}

For Robolectric to take into account our “test” injection, it must be informed of the Application class to be used as an argument to the @ Config annotation. The date used in DateUtilsTest.java is then injected into the application.

You can then test the MainActivity with several behaviors (several dates), without having to touch the production code.


That’s it !

I hope this article has helped you to better understand Dagger. If that’s the case, congratulations! If not, send me your questions as a comment. I will try to answer you as best I can. ;)

Until then….

..may the force be with you !