Skip to content

Getting Started with Dagger 2

Dagger 2 has been around for quite a while now and still holds up as one of the most valuable libraries for the ecosystem, and for good reason.

Android is one of those coding environments where tons of classes interact with each other endlessly. If one class requires an instance of another class for it to function, we call that class dependency. In an ideal world however, we would rather have these classes independent of each other for better reusability and testability.  You can’t test a dependent class without also having to test the class it’s dependent on.

With Dependency Injection, these dependencies can be identified at creation time and given directly to the previously dependent classes thus, eliminating any hard dependencies. In other words, classes get their dependencies from an external object. It also means mocks can be used to test these classes independently of the classes they depend on.

In other OTHER words, it turns this:

val car = Car()
val electricity = Electricity()
val tesla = Tesla(car, electricity)
tesla.run()

Into this:

val tesla = DaggerTeslaFactory.getTesla()
tesla.run()

Where’d Car and Electricity go, you may ask? Here’s the beginning of the Tesla class

class Tesla @Inject constructor() {
    @Inject lateinit var car: Car
    @Inject lateinit var electricity: Electricity

Dagger code is centered around annotations so everything is quick and simple. We’ll explore each one we can use a little bit further down.

Why use Dependency Injection?

Dependency Injection has quite a few great benefits:

  • Better code reusability and testability
  • Reduces boilerplate code
  • Promotes logical abstraction of code
  • Loose coupling

Add the Dependencies

Stick this in your app/build.gradle file.

kapt {
    generateStubs = true
}

dependencies {
    kapt 'com.google.dagger:dagger-compiler:2.16'
    implementation 'com.google.dagger:dagger:2.16'
    implementation 'com.google.dagger:dagger-android:2.16'
    implementation 'com.google.dagger:dagger-android-support:2.16'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
}

Dagger Annotations

Annotations play the main part in telling where dependencies are injected.

@Inject

class Tesla @Inject constructor() {
    @Inject lateinit var car: Car
    @Inject lateinit var electricity: Electricity

 

This is the basic annotation that tells us we want a dependency in another class. You can guess injecting Car and Electricity is the dagger-equivalent of having instances of those classes but what’s with injecting the constructor? That just allows the class with that constructor to be constructed by Dagger.

@Singleton

@Singleton class Car @Inject constructor()

 

The @Singleton annotation just tells dagger to create and maintain a single instance of that class. That means if Car is needed anytime, the same instance will be passed on.

@Module and @Provide

@Module
class TeslaModule {

    @Provides
    fun provideCar(): Car {
        return Car()
    }

    @Provides
    fun provideElectricity(): Electricity {
        return Electricity()
    }

}

 

Use these annotations together to make dependencies available in our main code.

@Component

@Singleton
@Component(modules = [TeslaModule::class])
public interface TeslaComponent {
    fun buildTesla(): Tesla
}

 

A component makes use of Modules we create to generate classes which have their dependencies injected. In this case, our TeslaFactory component will generate Teslas and handle the injection of Car and Electricity objects.

And the final step, to access the component in your main code:

val component = DaggerTeslaComponent.builder()
.teslaModule(TeslaModule())
.build()

And there you have it! The fundamental steps into learning Dagger 2.