Dagger 2 for Android, Part II ー The Basic Usage

Tan Jun Rong avatar

Tan Jun Rong

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:

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 👇

Mockup of Specification
Mockup of Specification

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 or abstract 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 either provision or members-injection contracts

Members-injection methods
Members-injection methods have a single parameter and inject dependencies into each of the Inject-annotated fields and methods of the passed instance. A members-injection method may be void 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 👇

Injected Jim with Dagger 2
Injected Jim with Dagger 2

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 avatar
Written By

Tan Jun Rong

Android Programmer who likes writing blogs, reading, coffee, snowboarding.
Published in Android
Enjoyed the post?

Clap to support the author, help others find it, and make your opinion count.