Dagger, but for 3 year olds — Part 2

Now you can Dagger too

Prafull Mishra
ProAndroidDev

--

Nothing is hard. Hard is nothing.

If you directly stumbled on this article, I suggest you to visit my previous post, upon which we will be building on in this read.

Previously we saw what are the concepts at work in Dagger and the various components that makes it work on Android (to implement DI). I also left a few pieces of code to work/think (/cry?) upon. So let’s pick it up from there.

Little Recap

I wrote about Dagger’s various entities in previous post which I would like to add here too, because these are vital to the process.

Modules are responsible for constructing the various objects or dependencies. In LEGO-analogy, Modules are the machines that assemble LEGO pieces to make your favorite food items. In Android world, this means methods that construct & return the required objects.

Components are the bots, that deliver the assembled LEGO food to you, i.e. it tells where to take the above dependencies. In Android, this translates to methods, with injectable classes (target) specified in its parameters.

Modules

Modules consists of methods to construct some object. For example:

The @Module annotation tells Dagger that this class is a Module. So if Dagger needs any dependency to be fulfilled it will search whether this class contains any method to provide an object of that type.

The name Module seems not so apt to me. Instead I think of it as a “Provider” which Provides the dependencies. And because of this all the provider methods inside Module class are annotated with @Provides. This tells Dagger that these methods are the actual pieces of code that can construct objects.

If you want an object to be constructed only once (ex. Database), you can add the @Singleton annotation along with @Provides.

Components

Components tell Dagger about the targets whose dependencies need to be fulfilled (with the help of Module).

For example, say we have a Activity which needs a Database instance. When we know that an instance is to be provided by Dagger, we annotate it with @Inject. Just like in the snippet below:

lateinit serves the purpose well here

Note: Private fields cannot be injected by Dagger

So the above Activity is basically a “target”. After knowing what our target is, we can now write the Component which will fulfill these dependencies using the (previously defined) Module. So the Component will be written as:

I know, it can be overwhelming to look at. So just focus on these observations:

  • Components are interfaces, not class
  • Component interface is annotated with @Component
  • With @Component, we define the modules through which this component will fulfill required dependencies from. It can be multiple.
  • Each injectable target need to be defined as parameter in interface methods. For example fun inject(activity: MainActivity) . The name of this method is irrelevant, but its best to name it inject()
  • Components can have nested interface which creates the component itself. These are annotated with @Component.Builder.

Let’s discuss a little more about Component Builder.

If you go back over to the Module which we wrote, you will find that even though we have written method to generate an instance of DB, it requires a context to create DB from. But how it will be provided to that Module? This is where Component Builder comes in.

Alongside construction of the Component, Component Builder also accepts any dependencies required by the Provider methods of Module . For example, creating a DB object requires a context which is stated in our module :

@Provides                         
fun getDatabase(context: Context): Database {
return Database.from(context).create()
}

For this, there is a method in Component Builder which accepts Context

@Component.Builder
interface Builder {
fun build(): Component

@BindsInstance
fun appContext(context: Context) //here we define context
}

Finally when we build this project, using this Dagger will generate a component for us, which will be an implementation of the above interface as:

component = DaggerComponent.builder()                                            
.appContext(this) //builder accepts context
.build()

We declared this in Application class, so that it can be invoked by not just one, but all activities or fragments or any injectable target. Feel free to move it as per your need.

Note: Wherever you see interfaces being used, for all of those, Dagger will generate implementations on its own, and that is why our component is initialized using DaggerComponent class even when our component was an interface named just Component.

The big picture would look like:

connection diagram of Dagger and its entities
Connection between various entities in Dagger 🔍

You can map all that we discussed, with this diagram and figure out how things are interconnected when working with Dagger.

Congrats! 🎉 You now understand Dagger and its working. Good enough to proceed with it on your own now. Effectively, you are 4 years old now :)

Of course the road does not ends here. This was just the tip of iceberg. There are tons of other things in Dagger to know about — Custom Scopes, Named dependencies, Sub component, injection types and many more which are out of this article’s scope.

I hope you were able to finally get onboard with Dagger, if yes don’t hesitate to clap 👏🏻 and share with others too! I will also try to answer any doubts or queries that you might have.

All hail DI !

--

--