Dagger 2. Part three. New possibilities

Evgenii Matsiuk (Eugene Matsyuk)
ProAndroidDev
Published in
11 min readAug 8, 2017

--

Dagger 2 articles cycle:

  1. Dagger 2. Part I. Basic principles, graph dependencies, scopes.
  2. Dagger 2. Part II. Custom scopes, Component dependencies, Subcomponents.
  3. Dagger 2. Part three. New possibilities.

Hello everyone! The third article on Dagger 2 has finally arrived!

Huge thanks for your reviews and comments. I’m really glad that these articles series helping you to dive into the world of Dagger. This is the source of strength and motivation for me to continue writing further.
In this third part, we’ll look at various interesting and important features of the library that you might need in the future.

Although the library has been around for quite a while, its documentation is still extremely poor. I would even advise to those developers, who are just starting their acquaintance with Dagger, not to look at the official documentation, in order to not get disappointed in this tough and unjust world.

Definitely, there are parts that covered more or less thoroughly. However, all the new features are lacking proper documentation, thus, I had to literally dive into the generated code to figure out how things work. Fortunately, there are good fellows who write good articles, but even they don’t give a clear and sharp answer sometimes.

So, enough ranting, let’s get forward to the new knowledge!

Qualifier annotation

It often happens that we need to provide several objects of the same type. For example, we want to have two Executors in the system: one single-threaded, and the other with CachedThreadPool. In such cases “qualifier annotation” will be very handy. This is custom annotation that has @Qualifier annotation. It sounds a bit like “salt is salty”, but everything is much simpler.

Basically, Dagger2 already provides us with one “qualifier annotation”, which, perhaps, is quite enough for most of the cases:

Now let’s see how it looks in action:

As a result, we have two different instances (singleExecutor, multiExecutor) of one class (Executor). This is exactly what we need! Note that objects of the same class with @Named annotation can also be provided by completely different and independent components as well as by dependent ones.

Postponed initialization

One of the common development problems is the long application startup. Usually there is a single reason — we load too much and initialize at startup. In addition, Dagger2 builds a dependency graph in the main thread. But often not all of the objects provided by Dagger are required immediately. So the library allows us to postpone the initialization of the object till the first call using the Provider<> and Lazy<> interfaces is made.

Let’s look at the following example:

Let’s start with Provider<Executor> singleExecutorProvider. Prior to the first call of singleExecutorProvider.get() Dagger does not initialize the corresponding Executor. But with each subsequent call to singleExecutorProvider.get() a new instance will be created. Thus, singleExecutor and singleExecutor2 are two different objects. This behavior is essentially identical to the behavior of an unscoped object.

So in what cases the Provider is relevant? It comes handy when we provide some kind of mutable dependency, which changes its state over time, and with each call we need to get the current state. “What the crooked architecture is this?” — you might say, and I will agree with you. While working with legacy code you often see something like this.

It’s worth noting that the authors of the library also advise not to abuse the Provider interface in places where the common unscoped provider is enough, as it can lead to above-mentioned “crooked architecture” and difficult-to-catch bugs.

Now about Lazy<Executor> multiExecutorLazy and Lazy<Executor> multiExecutorLazyCopy. Dagger2 initializes the corresponding Executor only on the first call to multiExecutorLazy.get() and multiExecutorLazyCopy.get(). Next, Dagger caches initialized values for each Lazy<> and will return cached ones on the subsequent calls to multiExecutorLazy.get() and multiExecutorLazyCopy.get().

This way, multiExecutor and multiExecutor2 refer to one object, while multiExecutor3 to the second one.

But if we add @Singleton annotation to provideMultiThreadExecutor() method in AppModule, then the object will be cached for the entire dependency tree, and multiExecutor, multiExecutor2, multiExecutor3 will refer to the same object.

Be careful.

Asynchronous loading

We approached a very nontrivial task. What if we want the construction of a dependency graph to take place in the background? Sounds promising? Yes, I’m talking about Producers.

Honestly, this topic deserves a separate consideration. There are many features and nuances there, enough good materials have been published already. Now I will only cover pros and cons of Producers.

Pros. Well, the most important plus is the download in the background and the ability to manage this download process.

Cons. Producers depend on Guava, which means plus 15k methods to the apk. But the worst thing is that using Producers will spoils the overall architecture of the app and makes the code more confusing. If you already had a Dagger, and then decided to move the objects initialization to the background, it will require extra efforts.

There is a special section devoted to the subject in the official documentation. However, I would highly recommend the articles by Miroslaw Stanek. He has a very good blog, and there are lot of articles about Dagger2. I even borrowed a few pictures in the past articles from him.
He writes about Producers in this article.

And in the next article there is a very interesting alternative for loading the dependency tree in the background, where RxJava comes to aid. I really like his solution, as it completely denies the drawbacks of using Producers, while solving the issue of asynchronous loading.

However, there is only one deficiency: Miroslaw not quite correctly applies Observable.create (…). But I wrote a comment about it in the article, so please pay careful attention to it.

And now let’s see how the the scoped object code looks like (with the “correct” RxJava):

Please, pay attention to @Singleton and the Lazy interface in AppModule. Lazy guarantees that the heavy object will be initialized at request time and then cached.

And what should we do if we want to receive a new copy of this “heavy” object each time? Then we need to modify AppModule:

For provideHeavyExternalLibrary() method, we removed the scope, and in the provideHeavyExternalLibraryObservable(final Provider<HeavyExternalLibrary> heavyExternalLibraryLazy) we use the Provider instead of Lazy. As a result, heavyExternalLibrary and heavyExternalLibraryCopy in MainActivity are different objects.

And it is also possible to move the whole dependencies tree initialization process to the background. You want to know how? Very easy. First look at how it was (from Miroslaw article):

And now look at the updated method void setupActivityComponent() (with my edits on RxJava):

Measurements

In the last section, we talked about the application startup performance. However, we know that if the question is about performance and speed, we need to measure! We shouldn’t rely on intuition and “it seems become faster” feeling. And Miroslaw helps us again in this and this articles. What would we do without him, I can’t imagine.

New interesting opportunities

New interesting features are showing up in Dagger, promising us to make our life easier. But it wasn’t an easy task to understand how everything works and what it gives us. Well, let’s begin!

@Reusable scope

This is an interesting annotation. It saves memory, but it is in fact not limited by any scope, which makes it very convenient to reuse dependencies in any components. I.e. it is a something between scope and unscope.

There’s a very important point in the docs, which somehow does not catch the eye initially: “For each component that uses @Reusable dependency, this dependency is cached separately.” And my addition: “Unlike the scope annotation, where the object is cached at creation and its instance is used by child and dependent components.

Let’s look into the example right away:

Our main component:

AppComponent has two Subcomponents. Did you notice FirstComponent.Builder construction? We’ll talk about it later.

Now let’s see at UtilsModule:

NumberUtils is with annotation @Reusable, and StringUtils is unscoped.

Next, we have two Subcomponens:

FirstComponent injects only in MainActivity and SecondComponent injects in SecondActivity and ThirdActivity.
Let’s see code:

Briefly about the navigation. From MainActivity we get into SecondActivity, and then into ThirdActivity. And now the question. When we are already on the third screen, how many NumberUtils and StringUtil objects will be created?

Since StringUtils is unscoped, three instances will be created, i.e. with each injection a new object is created. We know that.

But there will be two NumberUtils objects — one for FirstComponent, and another for SecondComponent. And here again I will give the basic idea about @Reusable from the documentation: “For each component that uses the @Reusable dependency, this dependency is cached separately!”, In contrast to the scope annotation where the object is cached at creation and its instance is used by the child and dependent components.

But the Googlers themselves warn that if you need a unique mutable object, then use only scoped annotations.

I’ll also give you a link to the question on SO about comparing @Singleton and @Reusable.

@Subcomponent.Builder

A feature that makes the code more beautiful. Previously, to create @Subcomponent we had to write something like this:

I did not like this approach because the parent component was loaded with unnecessary knowledge about the modules that the child subcomponents use. Plus passing large numbers of arguments does not look nice, because there is a pattern Builder for such purposes. Now it is a beauty:

Now, it is much better =)

static

Now we have an opportunity to do something like this:

That is, the methods responsible for providing dependencies in modules can be made static. At first I did not quite understand, why do I need it at all? And it turns out that a request for such a feature existed for a long time, and there are situations when it is useful.

The was a good question asked on SOF on this topic: “What actually differs @Singleton from @Provide static?”. To understand this difference well, you need to read the answer to the question, while parallelly experimenting and looking at the generated code.

Suppose we have three different versions of the same method in a module:

At the same time, authManager.currentUser() can provide different instances at different times.
The logical question is: how these methods differ.

In the first case we have a classic unscope. Each request will be given a new instance of authManager.currentUser() (more precisely, a new link to currentUser).

In the second case, a link to currentUser will be cached at first request, and will be given at each subsequent requests. I.e., if currentUser has changed in AuthManager, then the old link to the invalid instance will be given.

The third case is more interesting. This method’s behavior is similar to unscope, i.e, for each request a new link will be given. This is the first difference from @Singleton, which caches objects. Thus, placing object initialization in @Provide static method is not entirely appropriate.

Then what is the difference between @Provide static and unscope? Suppose we have following module:

AuthManager is provided by another module as a Singleton. Now quickly look through the generated code at AuthModule_CurrentUserFactory (in Android studio just put the cursor at currentUser and press Ctrl + B):

And if you add static to currentUser:

Then we get:

Note that there is no AuthModule in static version. Thus, the static method is called by the component directly, bypassing the module. And if there are only static methods in the module, then the module instance is not even created.

That is efficiency and no unnecessary calls. Actually we have a performance gain here. It is also known that calling a static method is 15–20% faster than non-static counterpart. If I’m wrong, Alexander Efremenkov will correct me. He knows for sure, and if necessary, he will do proper measurements.

@Binds + Constructor Injection

Mega-convenient union, that significantly reduces boilerplate code. When I just started learning Dagger, I did not understand why we need constructor injections. Where objects are coming from? And then there was @Binds. But everything is actually quite simple. Thanks to Vladimir Tagakov and this article for help.

Let’s consider a typical situation. There is a Presenter interface and its implementation:

We provide everything inside the module and inject the interface of Presenter into the Activity:

Let’s say that our FirstPresenter needs helper classes, to whom it delegates part of the work. To do this, we need to create two more methods in the module that will provide new classes, then change the FirstPresenter constructor, and consequently update the corresponding method in the module.

The module will be as follows:

And the same thing happens every time you want to add some classes and share it with others. The module gets dirty very quickly. Also there is too much code, isn’t it? But there is a solution that significantly reduces the code.

First of all, if we need to create a dependency and give a finished class, rather than an interface (HelperClass1 and HelperClass2), we can use a constructor injection. It will look like this:

Note that @FirstScope annotation has been added to the classes, so that Dagger understands which dependency tree to assign these classes to.

Now we can safely remove HelperClass1 and HelperClass2 providers from the module:

How else we can reduce the code in the module? Here we apply @Binds:

And add the constructor injection inside FirstPresenter:

What is new here? FirstModule and provideFirstPresenter have become abstract. @Provide annotation has been replaced with @Binds. And we pass not necessary dependencies, but a concrete implementation to the arguments!
Scope annotation was added to FirstPresenter @FirstScope, by which Dagger understands where to put this class. Also, @Inject annotation was added to the constructor. It has become much cleaner, and now it’s much easier to add new dependencies!

A couple of valuable additions to abstract modules from Andrei Zajac.
Let’s remember that FirstModule refers to FirstComponent, which in turn is a subcomponent of AppComponent. And to create FirstComponent we did this:

But how do we create FirstModule instance, if it is abstract? In previous articles, I mentioned that if we do not pass anything to the module constructor (i.e. we use default constructors), then we can omit these modules initialization when creating a component:

And Dagger itself decides what to do with abstract and non-abstract modules and how to provide all these necessary dependencies.

Also note that if a module has only abstract methods, then it can be implemented via an interface:

Furthermore, we can only add static methods to abstract modules, but not “Normal” ones:

What else haven’t been covered

Next, I’ll give you a list of features with short descriptions and links to a qualitative resources:

  1. Muitibindings. Allows to bind objects in a collection (Set and Map). Suitable for implementing “plugin architecture”. I highly recommend this very detailed article for beginners. More interesting Multibinding application cases of the can be found in Miroslav’s articles here and here. And the link to the official documentation in addition.
  2. Releasable references are useful when you have a memory shortage problems. With the help of corresponding annotations, we mark objects that can be sacrificed in case of memory deficit. It can be considered as a hack.
    In the docs (Releasable references subsection), everything is quite clearly described, oddly enough.
  3. Testing. Of course, for Unit-testing Dagger is not needed. But for functional, integration and UI tests, it may be useful to substitute certain modules. Artem Zinnatullin greatly explains the subject in his article and example. A section on testing is highlighted in the documentation. But again, Google can not properly describe how to substitute the component. How to create mock modules correctly and substitute them. To replace a component (individual modules), I use Artem’s way. Yes, it would be cool if it was possible to create test components and modules in separate classes and nicely include all of them in the test Application file. Maybe someone knows?
  4. @BindsOptionalOf. Works with Optional from Java 8 or Guava, which makes this feature difficult to access for us. If interested, you can find a description at the end of the documentation.
  5. @BindsInstance. Its main message is to stop transferring any objects through module’s constructor. A very common example is when a global Context is passed through the AppComponent constructor. So with this annotation, you do not need to do it. At the end of the documentation there is an example.

That’s it! It seems all the moments were covered. If something is missing or insufficiently described, write me up! Will fix that.

Authors:
Eugene Matsyuk
Roman Yatsina
Atabek Murtazaev

--

--