Dagger.Hilt TLDR (updated for 2.31.2-alpha)
A very short tutorial on how to use Dagger.Hilt if you don’t have time to read through the docs.
TLDR 2 minutes
Add gradle dependencies:
// project's build.gradle
dependencies {
// your other dependencies// Dagger Hilt
classpath "com.google.dagger:hilt-android-gradle-plugin:2.31.2-alpha"
}// app/build.gradle
dependencies {
// other dependencies
// Dagger Hilt
kapt "com.google.dagger:hilt-android-compiler:2.31.2-alpha"
implementation "com.google.dagger:hilt-android:2.31.2-alpha"
}
Apply these two gradle plugins to your app/build.gradle
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
Create your own Application class, can be as simple as this:
@HiltAndroidApp
class MyApplication : Application()
Notice the @HiltAndroidApp
annotation, which is necessary for the dependency injection to work.
Register your application class in your AndroidManifest.xml:
<application
android:name=".MyApplication"
// other attributes
>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Annotate constructor of the dependency you want to inject using @Inject
annotation:
class SomeDependency @Inject constructor() {
val value = "I come from injected dependency"
}
Finally, inject the dependency to your activity or fragment:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var someDependency: SomeDependency
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView.text = someDependency.value
}
}
Done! You can run your project, someDependency
contains an instance of SomeDependency
, that was injected for you by Dagger.Hilt. Notice the @AndroidEntryPoint
annotation of the class, that you need to add to any Activity
or Fragment
you want to inject dependencies into.
I have a multi-module architecture and want to support dependency injection
Actually this is it. The dependency itself could come from any gradle module (provided you depend on the module in gradle).
TLDR 5 minutes
Ok, so you want to depend on an interface, not an actual implementation of it. What do you do?
Let’s change our SomeDependency
class to implement some interface:
interface SomeInterface {
val value: String
}
class SomeDependency @Inject constructor() : SomeInterface {
override val value = "I come from injected dependency"
}
And change activity code so that it depends on the interface:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var someInterface: SomeInterface
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView.text = someInterface.value
}
}
Ok, running this you’ll get:
[Dagger/MissingBinding] com.example.daggerhilttldr.SomeInterface cannot be provided without an @Provides-annotated method
You need to create a Dagger @Module
, class that will tell Dagger, which implementation of SomeInterface
to actually inject.
This is an example module:
@Module
@InstallIn(SingletonComponent::class)
interface AppModule {
@Binds fun bindsSomeInterface(impl: SomeDependency): SomeInterface
}
The module needs to be annotated by @Module
annotation, that comes from Dagger 2. This annotation means, that the class or interface annotated by it provides clues for Dagger, how to construct certain dependencies. For example how to construct instances of an interface, should some class depend on that interface.
The module also needs to be annotated by @InstallIn(SingletonComponent::class)
. This annotation tells, that dependencies defined here can be accessed by any class in the app. You could install the module into some other @Component
and thereby control which classes of your app have access to which dependencies, but this topic is outside of the scope for this tutorial and most projects probably won’t need it anyway. You can learn more in the docs.
I want to share the same instance of SomeInterface across the whole app
Ok, easy, just add @Singleton
annotation in the @Binds
method signature, like this:
@Binds @Singleton fun bindsSomeInterface(impl: SomeDependency): SomeInterface
Bonus: I want to inject ViewModel
into my Activity
/Fragment
Provided you already have added all relevant gradle dependencies, you’ll need to add some new Android Jetpack dependencies:
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha07'
Now if you want to inject dependencies into your ViewModel
, use the standard@Inject
annotated constructor, like you would in other classes. On top of that annotate the whole class with @HiltViewModel
:
@HiltViewModel
class MainViewModel @Inject constructor(
private val someInterface: SomeInterface
) : ViewModel() {
val state = someInterface.value
}
Adding the ViewModel
to activity/fragment is trivial:
private val viewModel: MainViewModel by viewModels()
The whole activity class:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView.text = viewModel.state
}
}
If you’ve tried to use dependency injection with Dagger 2 on Jetpack ViewModel
, you know how painful it was. Now it’s easy, using just this one annotation @ViewModel
inject. The library takes care of the rest for you automatically.
But Dagger.Hilt is still in alpha!
It is. But it already works well. Being in alpha mostly means, that some APIs could change, but the simple use cases presented here are in my humble opinion unlikely to change. Personally, I’d recommend using Dagger.Hilt over Dagger.Android at this point, but should you for whatever reason decide to go with the bit older Dagger.Android and don’t know where to start, I’ve got you covered.
I’ve created a demo project and published it on Github, if you’d like to get hands on working implementation. You can view changes by commits, they roughly represent steps described in this article to integrate Dagger.Hilt to your app: https://github.com/lukas1/Dagger.Hilt.TLDR