Dagger 2 on Android: Assisted Injection

The missing piece in your Dagger setup

Fred Porciúncula
ProAndroidDev

--

Photo by Seth Macey on Unsplash

Whenever I work with MVP, it always bothers me how tricky it gets to properly inject the view into the presenter. Adding the activity to the graph was never a simple task (hello subcomponent verbosity, my old friend), and even though dagger-android does help with that, it also comes with its own verbosity.

If we're talking about MVVM, this is still relevant. Even though we wouldn't be interested in injecting the view in the view model, we might want to inject something that comes from the activity intent, for instance.

In the end, we just want to move away from setters that must be called right after a dependency is injected. We want to truly take advantage of Dagger's compile-time safety and we want our dependencies ready to go as soon as they're injected.

And we want all this with ease of use, no boilerplate and minimum verbosity.

UPDATE: Dagger now has built-in support for assisted injection

As of version 2.31, Dagger supports assisted injection out of the box. It works exactly the same as it's described here with the AssistedInject library with a couple of differences:

  • There's no need anymore for a separate library.
  • We don't need to define an @AssistedModule anymore.

You can check the docs here.

Behold: AssistedInject

It turns out there's a library that has been created to solve this exact problem:

Here you can find a presentation from Jake Wharton at Droidcon London 2018 about it:

The goal of this post is to be a quick and straight to the point introduction to the library. We'll go through an example and we'll see how it works. If you want a full understanding of the topic, please check the video above.

Let's get started

First off, you can check the code right away if you want:

It's a simple app that receives an input in the MainActivity and shows that input slightly modified in the TextActivity. Our TextPresenter will have 3 dependencies: the view, the text from the first activity (which will be passed through the intent), and an object responsible for modifying the text as needed.

The first thing we need is to introduce these to our build file:

The reason we don't see a 1.0 there yet is because Dagger will (hopefully) soon introduce a better way for third party libraries like this to integrate with it, so things will get even better. See this issue.

Then let's start with the presenter:

There's a lot going on there so let's go step by step:

  • The constructor needs to be annotated with @AssistedInject instead of @Inject.
  • The view is the activity itself, and the text comes from its intent. These won't be part of the graph, so we annotate them as @Assisted.
  • The TextDecorator is an ordinary dependency that is already part of the graph, so we don't need to give any special attention to it.
  • Then we need a factory interface inside the presenter annotated with @AssistedInject.Factory. It should have a single method that receives all assisted parameters and returns our presenter.
  • String is a pretty common type, so it's always a good idea to use a qualifier. That's what that @Text annotation is.

The library will generate an implementation of that factory. Instead of injecting our presenter directly, we'll inject the factory and get the presenter through it. Pretty neat, right?

Unfortunately, right now we'll still need to deal with modules in order to include our factory in the graph:

We don't necessarily need a dedicated module for it, even though that's a valid option. But we could also have those annotations in another module that is already part of our Dagger component. The important thing here is that we only need to write this once! So it scales much better than subcomponents: instead of writing a subcomponent and a module for every single presenter, we'll just need to properly add the @AssistedInject and @Assisted annotations, and introduce the factory. It's a big win.

So if we already have something like an ApplicationModule, for instance, we could simply annotate it like this and we'd be good to go:

It's a little sad that we have to reference generated code, but hopefully by the time Dagger improves and AssistedInject hits 1.0, we'll be able to skip this step.

There's nothing special in the ApplicationComponent, the important thing is that it includes the module annotated with AssistedModule:

Now the factory is in my graph and I can just expose it in my component, like I'm doing there.

It's the same with the application class, nothing special there besides a nice extension to make things a little better:

And finally, this is how I can inject the presenter in the view now:

🤩

Wrapping it up

If you're only using subcomponents as a way to add the view and its friends to the graph, you should really consider using AssistedInject instead. It's less verbose, requires less boilerplate and it scales much better.

This covers the most obvious and straightforward use of AssistedInject on Android projects. But there's more! It can also assist us to properly inject dependencies into custom views. If this seems interesting, check the video!

Dagger is a powerful tool that can be used in many different ways for many different purposes. I like to stick to the ways where I can hide from its infamous verbosity, and I'm happy to say that AssistedInject became yet another tool in my belt to keep the verbosity at bay and simplify my setup even more. I hope you can also make good use of it.

--

--