How to use Dagger 2 on Android with Kotlin

Virtually everyone who wants to create code on Android in a decoupled and easy-to-test way, resorts to Dagger sooner or later.

Although there is something that works a bit differently when setting up Dagger in Kotlin, most of it is quite simple, and in a few steps I’m going to show you here today.

Also be aware that, thanks to the power of Kotlin, there are other ways to solve the injection, and even some libraries made exclusively in Kotlin for it.

But Dagger is still a viable option and one of the most versatile (if not the most).

Configuring the project to use Dagger 2

If you’ve already configured the Kotlin plugin in your project, all you need to do is configure kapt.

If you already used Dagger, you probably know apt. kapt is just the version for Kotlin, which creates the necessary self-generated classes for Dagger.

To configure it, you need to add the following to build.gradle:

kapt {
    generateStubs = true
}

You can add it just before the dependencies section. If you want, you can instead use the new experimental plugin, which is pretty stable already:

apply plugin: 'kotlin-kapt'

Now you just need to add the dependencies of the Dagger compiler (using kapt to not be included in the apk) and the actual library:

kapt 'com.google.dagger:dagger-compiler:2.5'
compile 'com.google.dagger:dagger:2.5'

Everything is ready to start using Dagger.

Main module implementation

As you may know, for the main graph you’ll need a Module and a Component.

The application module, in this simple example, will only return the instance of the application itself.

To do this we’ll create a class annotated with @Module, which will receive the application instance via constructor, store it in a property, and return it using a method annotated with @Provides @Singleton:

@Module class AppModule(val app: App) {
    @Provides @Singleton fun provideApp() = app
}

You can see that, even for this easy class, the code is much simpler than in Java.

Now we have to implement the Component, which needs an array of modules to load, and specifies who is going to be able to manually inject it:

@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    fun inject(app: App)
}

Just create the class App, which will be responsible of generating the graph:

class App : Application() {

    val component: AppComponent by lazy {
        DaggerAppComponent
                .builder()
                .appModule(AppModule(this))
                .build()
    }

    override fun onCreate() {
        super.onCreate()
        component.inject(this)
    }
}

The interesting thing to see here is that, thanks to the lazy statement, we can specify the value of the graph in the definition of the property, and thus get read-only access to that property.

The code defined by the property won’t be executed until component.inject (this) is done, so that by that time this already exists and can be created securely way.

One module implementation per scope

The modules by scope allow that part of the graph only to live during the lifetime of the object that creates it.

In this way, we can create subgraphs that live and die with an Activity, for example.

We would create our module with what we need:

@Module
class HomeModule(val activity: HomeActivity) {
}

A Subcomponent in a very similar way to the previous one, indicating that it’ll be injected into the HomeActivity:

@Singleton
@Subcomponent(modules = arrayOf(HomeModule::class))
interface HomeComponent {
    fun inject(activity: HomeActivity)
}

And a plus method in AppComponent, to indicate that this component can be added subcomponents of that type:

interface AppComponent {
    ...
    fun plus(homeModule: HomeModule): HomeComponent
}

Now, in the HomeActivity you only need to declare the subcomponent:

val component by lazy { app.component.plus(HomeModule(this)) }

And you can inject it after the setContentView:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    component.inject(this)
}

If you’re wondering where app comes from, it’s a extension property that looks like this:

val Activity.app: App
    get() = application as App

It’s simply a way to avoid having to do casting every time you access application if you have your own custom one.

Conclusion

Dagger 2 is also easy to use in Kotlin. You no longer have an excuse to implement a great decoupled architecture in Kotlin.

Mansi Vaghela LinkedIn Twitter Github Youtube