Understanding Dagger and dependency injection (DI) as a beginner

Shayne Kelly II
3 min readJan 7, 2019

When I started my first experience working in a professional Android codebase, I was in a bit over my head with what all of this Dagger stuff meant.

So you just type @Inject, and this external object just shows up in your class? But wait — you need a bunch of other files to do it? Why not just invoke the external object’s constructor in your class?

If you’re trying to understand what the heck is going on with Dagger before you understand why anyone would use it, you might be in for a tougher time.

Why dependency injection?

The basic idea is to decouple class implementations from their dependencies. An external class will take care of the creation of these dependencies and spread them around to all of the places they’re needed in your program. Therefore your class doesn’t have to worry about how to create dependencies or share them with other classes.

It’s especially useful when you have classes that need to be shared amongst many areas in the same program (such as a Retrofit API interface). This allows for better modularization and better testability.

How it works — the building blocks of dependency injection with Dagger

Let’s start with a simple example. I have a class called AppRepository, and I want to inject my Retrofit API into it. There are two main building blocks you need:

  • Component
  • Module

That’s it!

Components

Components take care of where to inject your dependencies. In here, you can specify with objects can be injected with the inject() method, for example:

fun inject(repository: AppRepository)

There’s just a bit more boilerplate to add the component builder.

Modules

Modules take care of the creation of your dependencies and can also hold them so that they can be spread around to the objects in your codebase that need them — the ones you specified in your component using inject(). They are providers, with methods denoted with the @Provides annotation. For example, you can build your Retrofit API in your module to be injected like so:

…but, wait! Where does this Retrofit parameter come from? We don’t even know who calls this method, how do we pass a parameter into it?

Dagger implements its injection by code generation. This has a performance advantage over reflection. Reflection in Java/Android can lead to a significant performance hit since it prevents the Java Virtual Machine from performing certain optimizations. However, Dagger’s code generation and annotation processing leads to increased build times.

Dagger will call this @Provides method using its generated code. You have two choices:

  • Pass the dependency in through the constructor of the module
  • Use another @Provides method for your method parameter

In this case, the simplest thing to do is add another @Provides method for my Retrofit class.

The other method is to pass the dependency in through the constructor of the module like so:

In my experience, passing in the dependency through the constructor is useful for Android dependencies that require Context (e.g. SharedPreferences). Using application context over activity context is preferred to avoid memory leaks. Then you can build your module in your Application (or Activity class) during the onCreate() method.

Application context is passed through the constructor of the module
Custom application class takes care of building the module and injecting necessary classes

Now, all that’s left is to declare our API interface into the AppRepository class and inject it by building the module and calling inject():

A different method for dependency injection in Android is to build the module in the Activity and then inject the activity. Then you can pass any dependencies to the ViewModel, Repository, etc. in through their constructor for testability.

Thanks for reading! If you have any questions or suggestions, please let me know.

--

--