Hilt is a new Dagger-based library for Android apps, providing an easy way to incorporate dependency injection with less boilerplate code. In this article, we’ll discuss how to use it.
Setup
First, add the Hilt Gradle plugin to the project’s root level build.gradle
file:
buildscript {
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
...
}
...
}
Next, update the module’s build.gradle
file:
apply plugin: "dagger.hilt.android.plugin"
dependencies {
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
...
}
...
As of the writing, the latest version is 2.28.3-alpha.
Annotate the Application
The app must have an Application
class annotated with @HiltAndroidApp
, which kicks off the code generation.
Assume this is what we have with Dagger:
class App : BaseApp(), HasAndroidInjector {
private lateinit var appComponent: AppComponent
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder().appModule(AppModule(this)).build()
appComponent.inject(this)
}
override fun androidInjector(): AndroidInjector<Any> = dispatchingAndroidInjector
}
Now it can be simplified as:
@HiltAndroidApp
class App : BaseApp()
Annotate Activities / Fragments / etc.
Instead of calling AndroidInjection.inject(this)
in the activity’s onCreate()
method, we need to annotate it with @AndroidEntryPoint
, e.g.:
@AndroidEntryPoint
class SettingsActivity : BaseActivity() {
@Inject
lateinit var settingsViewModel: SettingsViewModel
...
}
In addition to activities, we can also use the @AndroidEntryPoint
annotation with fragments (the one from AndroidX), views, services, and broadcast receivers.
Hilt Modules
Hilt modules are standard Dagger modules that must be annotated with @InstallIn
. This annotation declares the components that the module should be included in, e.g.:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
...
}
Note that we no longer need to pass a reference to the Application
to the AppModule
. Hilt does that for us!
Components
Unlike Dagger, usually we don’t need to define components directly. Instead, Hilt provides a set of predefined components with corresponding scope annotations.
For example, the SingletonComponent
has the same lifetime as an Android Application
. It is created when Application.onCreate()
is called.
Similarly, an ActivityComponent
instance is created when the corresponding Activity’s onCreate()
is called, and destroyed when onDestroy()
is called. As expected, Hilt provides the access to the Application
and also the corresponding Activity
. However, we need to manually convert it to the specific activity type if needed, e.g.:
@Module
@InstallIn(ActivityComponent::class)
object SettingsActivityModule {
@Provides
fun provideSettingsActivity(activity: Activity): SettingsActivity
= activity as SettingsActivity
...
}
Scoping
By default, all bindings are unscoped, meaning a new instance will be created each time requested. We can also scope a binding so that it will only be created once per component instance, e.g.:
@Module
@InstallIn(ActivityComponent::class)
object SettingsActivityModule {
@ActivityScoped
@Provides
fun provideSettingsPresenter() = SettingsPresenter()
...
}
Note that the scope must match its corresponding component, e.g. we can only add the @ActivityScoped
annotation to modules installed to ActivityComponent
.
Conclusion
Hopefully, this article gives enough information for you to get started with Hilt. In case you’re interested, here’s a pull request to migrate to Hilt from Dagger in my toy project.