Dependency injection has became very popular subject among software developers. And there is an excellent reason for that, as it helps writing highly maintainable code that is easy to test. This short article explains what dependency injection is, what problems can it solve, highlights central concepts behind it and presents how it can be implemented in Android project using Dagger 2 library.
Dependency injection is a technique where an object does not create objects that it needs (its dependencies), but they are delivered to it from the outside. For example, lets consider Garage that needs RollUpGarageDoor object to function properly.
In the first example, RollUpGarageDoor is being created in the Garage constructor.
Let’s change it, so the needed object will be passed (injected) to the constructor.
Another option is to pass it (a.k.a. inject it) using the setter, like in the code below
And that’s it – no more, no less. You did dependency injection. As you can see, you don’t need fancy frameworks for dependency injection as it is not overly complicated. And it has advantages.
First is that you can share dependencies. For example, if your Android application uses HTTP client you might want to share it, as creating new client objects is expensive and requires configuration any time you do so. Same thing applies when you use database client in your application.
Other is that you can configure your dependency externally as a class that needs to be given dependency wouldn’t need to know anything about it. It makes it easier to follow single responsibility principle – our class only cares about its goal, excluding configuration of objects that help to achieve it.
Another topic closely connected to dependency injection is dependency inversion. It says that classes should depend upon abstractions, not concretions. Sounds complicated but it is not. Sticking to Garage example – you might want to change RollUpGarageDoor to something else, for instance, SwingUpGarageDoor.
It means that it is better to have some degree of abstraction like an interface called GarageDoor that is implemented by RollUpGarageDoor, SwingUpGarageDoor and possibly in the future other types like SwingOutGarageDoor and SlideGarageDoor.
This approach makes our program a lot more flexible. It also simplifies creating mock objects as they just need to implement given interface.
Dagger 2 and Android
Rules mentioned above are relatively straightforward and easy to implement, but you might want to use a framework – especially as your program grows and managing dependencies on your own becomes tricky. Most frameworks not only do the heavy lifting for you but also implements extra logic that allows you to have a dependency that is a singleton or have dependencies lazy loaded.
Dependency injection frameworks for Java have a long history starting from Spring through Guice and Dagger to Dagger 2. We’ll focus on Dagger 2 as it is the newest, requires less configuration, works well with ProGuard and is actively supported, all while comparing to Dagger 1 – it’s predecessor.
Let’s consider you have Preferences object that helps you to deal with SharedPreferences across the application. This short example will show you how you could implement this.
The first thing to do is to add dependencies to your application’s build.grade file.
After that, we need to create module class with a @Module annotation that consists of all dependencies – those will use @Provide annotation. By adding @Singleton, we ensure that there will be only one Preferences object in the system.
Your next task is to create component – interface that has information about the module that we use and therefore dependencies that we declared and objects where we will use them. In our case it is MainActivity.
In the App class that extends Application, we will create appComponent.
And it is time to do the real work. This time we will use field injection. Need to do two things – first place @Inject annotation before field in question and second – run App.appComponent.inject(this) in the onCreate method.
And that is the end. Now it is possible to use Preferences across MainActivity.
Dependency injection and dependency inversion are quite simple yet powerful techniques that can help you to write better, cleaner code. In that respect the sooner you start using it the better. In a case of Android applications Dagger 2 is the tool to use as it offers many advantages comparing to its predecessors and offers a full range of features. Happy coding!