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 remains a perfectly valid option, and one of the most versatile (if not the most).
Disclaimer: In this article I won’t explain how Dagger 2 is used, this is already known. If you have any questions, I wrote some articles about dependency injection a while ago.
If all this passionate you as to me, I encourage you to sign up for my free training where I will tell you everything you need to learn about how to create your Android Apps in Kotlin from scratch.
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.
If you like what you’ve seen, I encourage you to sign up for my free training, where I’ll tell you everything you need to learn about how to create your own Android Apps in Kotlin from scratch.
Thanks! I’ve created a project that uses Dagger 2 in an almost identical way: https://melorriaga.wordpress.com/2017/03/25/kokemon-mvp-kotlin-unitui-tests/
Thanks! I think that is cleaner an initialization of the AppComponent like the following one:
class App: Application() {
companion object {
lateinit var graph: AppComponent
}
override fun onCreate() {
super.onCreate()
graph = DaggerAppComponent.builder().appModule(AppModule(this)).build()
}
}
What do you think?
It’s another alternative yeah. But I prefer using a val rather than lateinit var, which prevents from modifying the graph from the outside, and the state is more predictable. And from code perspective, there’s not much improvement, the number of lines is mostly the same.
Consider that probably you have to change the AppComponent for instrumentation tests. In this case a var is necessary.
If you need that, then it can be useful, yeah. You can use a variant for that, though. Anyway, both solutions work
It might be a wrong assumption, but isn’t it better to specify the attributes of the modules as Application and Activity, respectively, instead of as their more specific extensions App and HomeActivity? I might be wrong but it sounds more decoupled to me.
Yeah it would be more decoupled, but can’t think of an example where those modules are reused. If it’s helpful in your case, it’s definitely a good idea.
using the “` kapt { generateStubs = true } “` seems not necessary (anymore?) if i’m not mistaken.
Gradle will spit out “` ‘kapt.generateStubs’ is not used by the ‘kotlin-kapt’ plugin “` if in place.
more info: https://kotlinlang.org/docs/reference/kapt.html
and: https://kotlinlang.org/docs/tutorials/android-frameworks.html
Mmm could be, this changes a lot. I’ll review and update asap
great tutorial
but there are some missing parts in the code examples
i think it would be better if you put this code on github
This code is in fact inspired in a bigger one that it’s on Github. Maybe I should link it. You can check it out here: https://github.com/antoniolg/Bandhook-Kotlin/
Where is the full source? I cannot determine where those snippets should be placed.
Here you can find a sample App https://github.com/antoniolg/Bandhook-Kotlin/
“@Singleton” to HomeComponent caused a compilation error. Also “appModule” produced a deprecated warning.
I started using Kotlin & Dagger. Sometimes I get an error like error: cannot access NonExistentClass and no information whatsoever about which class is missing or where in the code the reference is made to the non existing class. This seems incredibly unhelpful and cryptic message. Have you ever seen messages like this with no information about the origin of the error? This seems like reason enough to be cautious using Kotlin and Dagger together. What do you think?
No, haven’t found anything like this sorry… In Kotlin 1.2.30, there’s a new option you can enable that will give you more info when there are issues with kapt. Maybe you can try it. Check here (under Kapt diagnostic locations): https://blog.jetbrains.com/kotlin/2018/03/kotlin-1-2-30-is-out/