DI 101 — Part 1
Dependency Injection for the Android platform
When we first start studying software engineering, we usually bump into something like:
Software shall be SOLID.
but what does that mean, in fact? Well, let’s just say that each character of the acronym means something really important for the architecture, such as:
- Single Responsibility Principle
- Open/closed principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle which is the core concept on which the dependency injection is based.
Simply, we need to provide a class with all the objects it needs in order to perform its duties.
Dependency Injection sounds like a very complex term for something that is, in fact, really easy and could be explained with this example:
As we can see, in the first case, we create the dependency in the constructor, while in the second case it gets passed as a parameter. The second case is what we call dependency injection. We do this so that our class does not depend on the specific implementation of its dependency but it just uses it.
Moreover, based on whether the parameter is passed over to the constructor or to a method, we talk either about constructor dependency injection or method dependency injection:
On Android, we have different choices when it comes to frameworks for solving this particular problem but the most famous is Dagger 2, first made by the awesome guys at Square and then evolved by Google itself. Specifically, Dagger 1 was made by the first and then Big G took over the project and created the second version, with major changes such being based on annotations and doing its job at compile time.
Importing the framework
Setting up Dagger is no big deal, but it requires us to import the android-apt plugin by adding its dependency in the build.gradle file in the root directory of the project:
Then, we need to apply the android-apt plugin in the top part of the app’s build.gradle file, right below the Android application one:
apply plugin: ‘com.neenbedankt.android-apt’
At this point, we just need to add the dependencies so that we are now able to use the libraries and its annotations:
The last dependency is needed because the @Generated annotation is not yet available on Android, but it’s pure Java.
For injecting our dependencies, we first need to tell the framework what we can provide (i.e. the Context) and how that specific object is built. In order to do this, we annotate a specific class with the @Module annotation (so that Dagger is able to pick it up) scan for the @Provide annotated methods and generate the graph that will give us the object we request.
Let’s see an example where we create a module that will give us the ConnectivityManager. So we need the Context that we will pass in the constructor of the module:
A very interesting feature of Dagger is providing a Singleton by simply annotating a method, dealing with all the issues inherited from Java by itself.
Once we have a module, we need to tell Dagger where we want our dependencies to be injected: we do this in a Component, a specifically annotated interface in which we create different methods, where the parameters are the classes into which we want our dependencies to be injected.
Let’s give an example and say that we want our MainActivity class to be able to receive the ConnectivityManager (or any other dependency in the graph). We would simply do something like this:
As we can see, the @Component annotation takes some parameters, one being an array of the modules supported, meaning the dependencies it can provide. In our case, that would be both the Context and the ConnectivityManager, as they are declared in the ApplicationModule class.
At this point, what we need to do is create the Component as soon as possible (such as in the onCreate phase of the Application) and return it, so that the classes can use it for injecting the dependencies:
In order to have the DaggerApplicationComponent automatically generated by the framework, we need to build our projects so that Dagger can scan our codebase and generate the parts we need.
In our MainActivity, though, the two things we need to do are annotate a property we want to inject with the @Inject annotation and invoke the method we declared in the ApplicationComponent interface (note that this last part varies based on what kind of injection we are performing, but for the moment we can leave it as is for the simplicity), so that our dependencies get injected and we can use them freely:
Of course, we could do Dependency Injection manually, managing all the different objects, but Dagger takes away a lot of the “noise” involved with such boilerplate, giving us some nice additions (such as Singleton) that would otherwise be pretty awful to deal with in Java.