Dagger 2 for Android, Part II ー The Basic Usage
This article is about the basic usage of Dagger 2 library for Android development. I will discuss the usage of basic Dagger 2 annotations: @Inject
, @Module
, @Provides
and @Component
.
This is part of my Dagger 2 blog series:
- Dagger 2 for Android, Part I ー What is Dependency Injection?
- Dagger 2 for Android, Part II ー Very Basic Usage (you are here)
- Dagger 2 for Android, Part III ー The @Qualifier and @Named Annotation
- Dagger 2 for Android, Part IV ー The @Scope Annotation
- Dagger 2 for Android, Part V ー @Inject for Constructor Injection
- Dagger 2 for Android, Part VI ー @Component.Builder and @BindsInstance
The code example used in this article will be written in Kotlin for Android development.
Let's begin without Dagger
Let's begin with something without using Dagger2. After that, we will rewrite it with Dagger2. So that we can compare the behavior
We'll start with a spec in mind. Pretend that we are creating a page to show a Person
's details 👇
We will need to have a Person
class:
class Person(val name: String, val pet: String, val favoriteColor: String)
Pretend we have an xml looking like this:
<LinearLayout ...
android:orientation="vertical" >
<ImageView ...
android:src="@drawable/person"/>
<TextView ...
id="@+id/nameTextView"
tools:text="Name: Jim..."/>
</LinearLayout>
To display this, we have to simply create a Person
object, and set the nameTextView
accordingly.
class MainActivity: AppCompatActivity {
val person: Person = Person(name = "Jim", pet = "Little Puppy", favoriteColor = "Blue")
fun onCreate() {
nameTextView.text = """
Name: ${jim.name}
Pet: ${jim.pet}
Favorite color: ${jim.favoriteColor}
""".trimIndent()
}
}
Nice and easy, no Dagger 2 involved.
Re-write using Dagger 2
Let's re-write with Dagger 2! Over here, we will learn about 4 annotation:
@Inject
@Provides
@Module
@Component
(Please follow the Dagger 2 setup guide for installation).
1. The @Inject
annotation
To inject something, we need to use @Inject
annotation. Instead of instantiating Person
object in MainActivity
, we replace it with @Inject
:
class MainActivity: AppCompatActivity {
@Inject
lateinit var jim: Person
fun onCreate() {
setContentView(...)
nameTextView.text = """
Name: ${jim.name}
Pet: ${jim.pet}
Favorite color: ${jim.favoriteColor}
""".trimIndent()
}
}
At this point, if we try to compile the code, we will get a runtime error, because jim
will be null
, because we haven't tell Dagger how to provide a Person
object.
2. The @Module
and @Provide
annotation
Now we want to teach Dagger 2 how to provide the Person
to be injected.
To provide a dependency, we need to make a module class. Module
is a class which provides the needed dependency.
Since we are creating a module
which provides Person
dependency, we will name it PersonModule
. But note that the class name is not important, the important part is the annotation @Module
:
@Module
class PersonModule {
...
}
After making the module
, we need to make a class inside the module with @Provide
annotation:
@Module
class PersonModule {
@Provides
fun providePerson(): Person = Person(name = "Dagger Jim", pet = "Dagger Little Puppy", favoriteColor = "Dagger Blue")
}
At this point, we have a module
that is able to provide Person
dependency, but it is not referenced anywhere in the code. So the next thing we need to do is to tell Dagger
to use this PersonModule
.
3. The @Component
annotation
To register Module
with Dagger, we will use the @Component
annotation.
Inside the argument of @Component
, we need to state (modules [PersonModule::class])
.
Again, the name of this interface
does not matter, important that it is properly annotated.
@Component(modules = [PersonModule::class])
interface AppComponent {
...
}
The component class is not yet finished, we need to add another method inside.
This inject
method will be used to inject Person
into our MainActivity
.
@Component(modules = [PersonModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
}
Optional reading for @Component
It is ok if you don't fully understand this part, but I want you to keep in mind about the documentation!
You might wonder why it's an interface
and not class. If you read the documentation of @Component
annotation, the first sentence says:
Annotates an
interface
orabstract class
for which a fully-formed, dependency-injected implementation is to be generated from a set of modules.
Following the documentation, it has to be either interface
or abstract class
.
If we read it further, it says
Component methods
Every type annotated with @Component must contain at least one abstract component method. Component methods may have any name, but must have signatures that conform to eitherprovision
ormembers-injection
contracts
Members-injection methods
Members-injection methods have a single parameter and inject dependencies into each of theInject
-annotated fields and methods of the passed instance. Amembers-injection method
may bevoid
or return its single parameter as a convenience for chaining. The following are all valid members-injection method declarations:
- void injectSomeType(SomeType someType);
- SomeType injectAndReturnSomeType(SomeType someType);
What we are doing is similar to void injectSomeType(SomeType someType)
, which is a member-injection method
.
So if you wonder where and how to know what Dagger is capable of, one way is by reading example online or blog posts such as this one, another good way is to read the documentation! 🔖
Ready to inject the Person
object!
Back to our MainActivity
, we need to add 1 more line of code to inject the Person
:
DaggerAppComponent.builder().build().inject(this)
class MainActivity: AppCompatActivity {
@Inject
lateinit var jim: Person
fun onCreate() {
setContentView(...)
/* ADD THIS LINE */
DaggerAppComponent.builder().build().inject(this)
nameTextView.text = """
Name: ${jim.name}
Pet: ${jim.pet}
Favorite color: ${jim.favoriteColor}
""".trimIndent()
}
}
Remember the method you add in AppComponent
? 👇
fun inject(activity: MainActivity)
Dagger
generated a method with the same method name.
Let's say you change the method from inject()
to inject_INTO_THIS_PLEASE()
in AppComponent
:
@Component(modules = [PersonModule::class])
interface AppComponent {
fun inject_INTO_THIS_PLEASE(activity: MainActivity)
}
Now you should use it like this 👇
DaggerAppComponent.builder().build().inject_INTO_THIS_PLEASE(this)
Please don't use method name like this 😅 💦
With this, your program should be working like before!
I actually wrote the code for real. Here's a screen capture 👇
Code is available in Github: will be made available soon!
Wrapping up
You might wonder, what's the point of using Dagger 2? That's a great question! But it's out of the scope of this post, this post main target is to teach about basic usage of Dagger2's annotation.
I will write a separate post to discuss about this, stay tune!.
Up next: Dagger 2 for Android, Part III ー The @Qualifier and @Named Annotation. 😀
Tan Jun Rong
Clap to support the author, help others find it, and make your opinion count.