As stated on the GitHub page, Dagger2 is a fast dependency injector for Android and Java. Although there are many articles explaining interesting points about Dagger2  (like this, this and this), I decided to write one by my own. The reason is that even after studying a lot about dagger (and implemented it in some projects for my learning purpose), when I tried to use it in a real application, I realized there were still many unclear concepts to me. So, I went back to my studies and created an app to explore those areas I was struggling with. Now I want to share that app and add some comments about it. Maybe it can save you some time.

Note that those are just my opinion and maybe they can be obvious to some of you.

Demo App Overview

This article is totally based on the demo app we provide. It implements everything we are discussing here. So, reading this post will make more sense if you download the app to follow the topics. Also we will not show too much code here, but talk about the topics and how we implemented them.

Basically it shows how to create a component graph with multiple component dependencies (three depth levels) and exposes objects from the parent components to be available on the children components (each level using different scopes). To do so, we created a login mechanism that uses a component that lasts only while an user is logged-in. Everything is destroyed when user is logged out. When a new user logs in, a new component is created. It also shows some components that lasts per activities and give them access to the presenter and repository layers so that we can create and delete tasks for each user.

Then, we demonstrate how to use lazy injection by simulating a heavy service initialization (a fake email provider) and finally show how to properly reconstruct a graph, that contains dependencies, in case of app is killed by the system and later restored.

To login on the app, you can use one of these users:

Note that we are not persisting any user task, so after restart the app or logout an user, all changes in the task repository will be lost.

Some Background

Component Dependencies

There are times we are not able to provide Dagger2 all required information to create our application component. This is when component dependency comes in handy. It allows us to break our application component into multiple small components that are independent each other. Then we can inform which component depends on another one by using “dependencies” element when declaring our component. Later, when have enough information, we can create a child component by providing all information it requires.

As an example of component dependency usage is to create a component that holds information for a user session. Another one would be components that are available only during an activity lifecycle.

Notice that by using component dependencies, those bindings provided by the parent component are not exposed to its children by default. If you want to use a parent binding in a child component, you need to explicitly expose it in the parent’s components.

Custom Scopes

Custom scopes are useful when we want a component to lasts for a specific lifecycle (ex. during an activity lifecycle). In fact, it is similar to @Singleton standard scope, but by using a custom scope, we can make things clear regarding to the component lifecycle. For example, a @UserSession custom scope clearly tells us it is intended to lasts while an user is available.

What I found out is that custom scopes are sometimes misunderstood. By the fact that it is supposed to lasts for a well defined period of time (and usually shorter than @Singleton scope), this does not mean that such components will die after that period. We, as developer, need to release all references that our components hold when we decide they are no longer needed. In short: we determine when creating and destroying a custom scope, as well as all their references. In other words, as found on this SO question, “scopes will exist as long as you keep a reference to them”.

Let’s see one example on how to declare three components with dependencies and different custom scopes, and also how to expose objects from the parents to the children:

  • ApplicationComponent: this is the parent component. It uses @Singleton scope.
  • UserComponent: depends on the ApplicationComponent. It uses @UserScope custom scope.
  • ActivityComponent: depends on the UserComponent. It uses @PerActivityAfterUserLoginScope custom scope.

As we can see above, there are three components: ApplicationComponent, UserComponent and ActivityComponent. Their dependencies are showed in the @Component annotation. Since we want to use Context object in our ActivityComponent (that is provided by the ApplicationComponent), we need to expose it on both ApplicationComponent and UserComponent. But if we try to use UserRepositoryImpl object in the ActivityComponent, we will get a compile time error, since it is only exposed in the ApplicationComponent, meaning it is accessible only by the UserComponent. If we really need to access it in the ActivityComponent, we should also expose it in the UserComponent.

We can also see the scopes each component uses. They clearly inform us on when they should be available.

Note: Dagger2 provides another way to break our component into multiples components. It is called subcomponents, but this is out of scope of this article. If you want to take a look on it and see the differences between both ways, check the official documentation and this SO question.

Demo App – Components and Custom Scopes

Below you can find a list of all components (and their scopes) used in the demo app. You can also see a brief explanation on when they are created (and also destroyed). We will not to show any implementation details in order to avoid this post to be lengthy, but you can check this out in the code.

  • ApplicationComponent: created by the application class when app starts. It exposes the context as well as objects to access user repository (these objects are used everywhere in the app, so this is the best place to put them).  It uses @Singleton scope meaning it will be available throughout the app lifecycle.
  • MainActivityComponent: when you open the app and there is no an user session available, this component is created by the UserLoginActivity. It uses @PerActivityBeforeUserLoginScope custom scope. It depends on the ApplicationComponent since it needs user repository access to validate the user login process.
  • UserComponent: created right after a successful login (by the MainActivity) and it is available until user is logged out. It is stored in the application class and uses @UserScope custom scope. It depends on the ApplicationComponent.
  • ActivityComponent: created whenever an activity is created (after a successfully login) and lasts while that activity exists. It provide access to the the presenter and domain layers and uses @PerActivityAfterUserLoginScope custom scope. It depends on the UserComponent (which depends on the ApplicationComponent) since it needs access to some members of it (e.g.: TaskRepository, EmailServiceProvider, etc).

Notice that we are using two different scopes (@PerActivityBeforeUserLoginScope and @PerActivityAfterUserLoginScope) that are supposed to live while activities are alive. We could have used just one for both components, but we preferred to do this way to make things clear (i.e.: when each one is supposed to be available).

Demo App – General Flow

This section is intended to demonstrate the general flow you will find in the demo app and where dagger fits in on each step. You may better understand it if you follow each step by checking the source-code.

When you first open the demo app this is what will happen:

  • ApplicationComponent is instantiated in the Dagger2DemoAppApplication::onCreate() method.
  • Then, MainActivity is created. At this time, Dagger injects UserRepositoryImpl on it.
  • Next, it will detect no user was previously logged-in and will create UserLoginActivity in order to show a login screen.
  • UserLoginActivitty will create a MainActivityComponent instance and request it to inject UserLoginPresenter instance on it. 

After you successfully login, many things happen:

  • UserComponent will be instantiated and stored in the Application class. It will be available while user is logged-in on the app. As soon as you make a logout, it will be destroyed. This is where we should release all references that UserComponent holds (e.g.: a database handler, a socket connection, etc). When you log-in again with another user (or with the same user), another UserComponent object will be instantiated.
  • You will be directed to the TaskListFragment. At this time, Dagger creates an ActivityComponent instance for this fragment (actually for the TaskListActivity that created TaskListFragment) and injects a TaskListPresenter member on it. In the TaskListPresenterImpl class we will see some objects being injected which will allow us to make operations with tasks for that user.
  • Still on TaskListPresenterImpl, we can see that EmailServiceProvider and TwitterServiceProvider will not be injected at first, since both use Lazy Dagger interface, meaning they will be injected only when user tries to use them.
  • If you long-click over a task, you will be able to remove it, send it by email or tweet it (just fake operations).
  • When you open TaskCreateActivity, another instance of ActivityComponent will be created for this activity.
  • Besides creating new tasks, you can also delete them. These operations are executed in the task repository layer by using objects (presenters, repositories, etc) that are injected by the Dagger. Check out the TaskListFragment and TaskListPresenterImpl classes for details.

Next time you open the app:

  • If you did not logout user previously, MainActivity will detect it by checking it in the user session repository (since an instance of UserSessionRepositoryImpl will be injected on it). Then, instead of showing you the login activity, it will create a UserComponent object and point you direct to the TaskListFragment (this is how we can create different components based on different event flows).
  • If you have logged out the user previously, UserLoginActivity will be shown to you.
  • Whenever you logout an user, you will be redirected to the login activity.

Lazy Injection

There may be cases that our objects initialization are expensive (e.g.: a service that requires an external server authentication). In these cases we can request Dagger2 to not initialize them until they are required. This can save time, for example, when initializing an activity by skipping a heavy service provider setup when creating an activity. Later, only if that service is required, we then initialize it. If that service is never requested, it will never be initialized.

For the user perspective this seems to be a nice approach because, instead of making user to wait for a long time when an activity is created, we can initialize specific services only when user really requests for them. Then, we can show, for example, a progress dialog while we initialize that service.

In order to put it in practice, we created an EmailServiceProvider class that simulates a heavy initialization (actually its constructor just sleeps for 5 seconds). Its constructor is called only when we first try to send an email. Then, when we call EmailServiceProvider ::sendEmail() method, it will first wait for the service initialization, and only then will send the email. A progress dialog is shown to the user, so that he can be aware of what is going on. Next time you try to send an email, you will notice that progress dialog will be visible for a very short time (i.e.: only when sending the email) since the provider was already initialized.

To use it we need to use Lazy<T> interface:

Later, when we decide it is time to inject it, we just call Lazy<T>::get() method. Next calls to Lazy<T>::get() will return the same instance:

App Killed By The System

Now your app is running fine. You made some tests and everything seems to be ok. But then you realize it crashes sometimes. You start investigating and cannot reproduce the crashes. Whenever they happen, you have no debugger attached to get the messages. After hours of  investigation, you notice this happen when your app is in the background and you try to restore it from the task switcher, but you are not sure, since it happens sporadically.

Ok. Don’t panic! If this is the case, you might be facing an issue when the system kills your app when it is in the background due to a need to free up resources. This is why you do not face that crash all the times you restore our app (but only when it was killed by the system).

If we take a close look on our graph, we can see that ActivityComponent depends on the UserComponent. UserComponent is created in two cases: when the app is started and an user was previously left logged-in, or after a successfully login. Then, TaskListFragment is presented to the user.

But if we switch to another app while TaskListActivity or TaskCreateActivity (or any other activity that could use ActivityComponent) is in the foreground, our app will be eligible to be killed by the system if it needs to free up resources. If that is the case, when we restore it, the system will try to re-create the last activity that was being shown when app went to the background. Let’s see how we create ActivityComponent (for both TaskListActivity and TaskCreateActivity classes):

Note that we need to pass an instance of UserComponent (remember that it was created when an user was successfully logged) that resides in our Application class. But when the system restores this activity, it will not restore our UserComponent. The reason is that it simply knows nothing about any UserComponent when restoring TaskListActivity (or TaskCreateActivity). So, we need to provide a way to find out whether UserComponent is already instantiated, and if not, we need to do it manually. If we do not do it, we will get a NPE since Dagger calls Preconditions.checkNotNull(…) method to ensure any dependent component is not null.

To do it, we created a method that creates UserComponent when it is null:

This code is pretty straightforward. In case UserComponent is null, we request ApplicationComponent to inject some members (i.e. UserRepository and UserSessionRepository) on it. Then, we get the current user from the UserRepository. After that we will have all required information to create an UserComponent instance that will be passed to the ActivityComponent constructor.

Bottom line is whenever we are creating a component that depends on another one, we first need to check whether this dependency component is already available, and if not, take the appropriate actions to first create it.

You can find further information about it on this article. Unfortunately I only found it after spend almost two days trying to figure out what was going on when facing this issue.

Conclusion

In this article, I just showed some cases I ran into when using Dagger in a real application. Still today I sometimes get stuck in front of some errors or wrong assumptions, but after figure out my mistakes, I can say that using Dagger is helping me a lot to make better apps. So I really hope these topics can save someone’s time.