Dagger, but for 3 year olds — Part 2
Now you can Dagger too
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:
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 themodules
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 itinject()
- 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:
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
!