
Android Dagger 2 Tutorial
This guide explains how to use Dagger 2 in Android. I will go through the simple setup, explain what the dependency graphs are and how to provide custom annotations.
You can find the final code in my GitHub repository:
Project Setup
I use Android Studio 3 Canary 7 in this tutorial.
In this tutorial we will use the following libraries:
- Dagger 2 https://github.com/google/dagger
Create two productFlavors which we’ll use later to show, how Dagger can improve injection based on external settings.
The resulting build.gradle can be found here:
https://github.com/matthiasbruns/android-dagger2-tutorial/blob/master/app/build.gradle
Since we use the annotationProcessor, be sure to activate “Annotation Processing” in Android Studio.
Intro
This guide is all about Dagger. To show you some advantages which come with dependency injection, I created some fake app model. We will have a LemmingsRepository, which loads Lemmings from somewhere. We will also have a Lemming model, which stores the lemming data.
Lemming
A simple pojo which stores data of a lemming in a class.
LemmingRepository
A repository is a source of data. You can provide an interface of a repository and implement as many repositories as you want.
This example is incomplete and does not provide ways to handle asynchronous datasources as REST endpoints.
If you want to know more about repositories and how to implement them correctly, have a look at my other guide:
We will implement two repositories:
- LemmingCacheRepository
- LemmingRestRepository
The Repository is a base interface which allows implementing classes to define the model and id class. It provides the usual CRUD operations.
The LemmingRepository defines the datatypes of the model and id generics.
The LemmingCacheRepository implements the LemmingRepository and stores the lemming data in memory. Again — don’t expect that this implementation is good or working — it is just an example.
The counter part to the cache repository is the LemmingRestRepository, which communicates with a rest api.
Dagger 2
Terminology
Dagger uses components and modules. A components combines modules into a set of dependency providers. Each module knows how to provide certain dependencies. Components should be used per domain and have defined lifecycles.
Dependency Graph
Let’s start to prepare the dependency graph. But what is the dependency graph? Dagger has no predefined structure and inheritance rules. You as a developer can decide which components depend on other components, what dependencies live during a component’s lifetime and which should be recreated each injection.
This is how the components depend on each other. The AppComponents creates singletons and app-wide dependencies. The LemmingComponents provides dependencies for the lemming domain.
ApplicationConfig
This class is used during injection to decide if some injection results use online or offline features. It will be used later during the AppComponent creation.
AppComponent
The AppComponent uses two modules:
- AppModule
- RepositoryModule
AppModule
The AppModule provides the application context and the ApplicationConfig, which are both stored as a singleton in the AppComponent. To allow other components which depend on the AppComponent to access the ApplicationConfig and the LemmingRepository in this component, we have to define two methods:
- ApplicationConfig config()
- LemmingRepository lemmingRepository()
If we wouldn’t define the methods, the LemmingComponent would receive new instances of both components. This would be a problem for the ApplicationConfig, because we need to have the same information in all classes.
The inject method describes, who is allowed to receive dependencies from this component. In this case, only the DaggerApplication can be injected.
The AppModule provides the application context, DaggerApplication and the ApplicationConfig. Each of them are provided as a singleton. The @Singleton annotation is the same as the one in the AppComponent. If the component’s annotation and the provides annotation match, the created object is treated as a singleton in this component. If you leave out the annotation in the provide methods, every time when there is an injection the module creates a new instance of the class.
RepositoryModule
The repository module decides, which repository will be injected. In our case we have a boolean flag in our config, which marks, if the online mode is enabled or not. The ApplicationConfig is injected by dagger. Since this module is used in the AppComponent, it receives the singleton config object provided by the AppModule above.
DaggerApplication
The DaggerApplication class prepares the Dagger dependency graph. It initializes the AppComponent and decides, if the app should use online features or not. If another component depends on this AppComponent, it can receive the created AppComponent through:
The AppComponent uses two modules, which need to be created during the component creation:
LemmingComponent
After we’ve prepared our AppComponent, we can start building other domain components. The LemmingComponent provides all dependencies required in the lemming domain of this app.
As you can see, we’ve marked this component as ActivityScoped. Since we depend on a Singleton-scoped component (AppComponent), Dagger forces us to create a new annotation with a weaker scope. Dagger can detect by itself, which annotations are weaker than others and builds an internal graph based on this information.
Each scope annotation looks like this and the only thing that differs is the name of the annotation.
Let’s head back to the LemmingComponent. This component uses one module — the ActivityModule and allows the LemmingActivity to be injected by this component.
ActivityModule
The ActivityModule provides a String method to show in an exemplary way, how you can decide which object to return based on app settings. In our out of the question useful example, we return “Online” or “Offline” based in the isOnlineAllowed() method in the ApplicationConfig.
A new annotation is introduced here
Dagger decides which method to call when injecting by its return type. If you have two methods with the same return type, you have to name them by adding a @Named annotation. In the class, which receives the injected objects, you need to add @Named() to the @Inject annotation to tell Dagger what to inject.
LemmingAcitivity
We inject two object into this activity
The LemmingRepository is provided by the AppModule in the AppComponent. The mTitleSuffix String comes from the ActivityModule in the LemmingComponent. Since the LemmingComponent depends in the AppComponent and the AppComponent allows the LemmingComponent to access the LemmingRepository, we receive the same instance as stored in the AppComponent.
The creation of the LemmingComponent looks like the creation of the AppComponent in the DaggerApplication class. Since we depend on the AppComponent, we have to get it from the DaggerApplication class.
Start the app in one of the two flavors. If you pick the online flavor, the activity will print “LemmingRestRepository” and the title will be “Dagger2 — Online”.
Switch to the offline flavor. The app should now show “LemmingCacheRepository” and “Dagger2 — Offline”.
Summary
I went through a quick example to show you how to use Dagger 2. The guide explained the dependency graph and scopes, how to create singletons and what is required to allow components to use dependencies from other components.
This guide picked created a case, where to product flavors needed different behaviour in some cases. Dagger had to decide, if it used the offline or online implementation of the LemmingRepository during injection and which String to return for the title in the ActivityModule.
Conclusion
Dependency injection is a good way to get rid of the singleton antipattern. When required, you can easily manipulate what is being injected in the providing methods and do not have to copy & paste big if-else constructs into classes. Classes do not need to know where “things” come from and how to get them anymore.
Originally published at gist.github.com.
