Hilt Migration Guide
Dagger is an advanced dependency injection library and it is not easy to understand at the first glance for most developers. Using Dagger in Android requires much more effort for framework classes since it requires some boilerplate code. Hilt helps to reduce Dagger’s complexity and boilerplates with a set of ready components, scopes, and annotations.
In this post, we will see the Hilt migration of a sample app that uses dagger-android for DI. The migration will be in the order as shown below.
- Application class
- Dagger components and modules
- Activities & Fragments
- ViewModels
- WorkManager
- Tests
You can also check out the changes from the Hilt migration pull request for the sample app used in this post.
Note: This migration relies on Hilt version
2.28-alpha
.
Before starting the migration, you need to add Hilt dependencies to your grade files.
Application Class
The first thing you will do is to add@HiltAndroidApp
annotation to the Application class.
It is better to keep the old dagger-android implementation in the application class since the migration will be done incrementally. It will give a chance to make a sanity check during the migration.
Dagger Components and Modules
Hilt provides ApplicationComponent
which is to replace components which are annotated with@Singleton
. In order to remove the AppComponent
in this sample app, all modules are annotated with InstallIn(Applicationcomponent::class)
.
After adding the annotation to all modules, AppComponent
could safely be removed.
Additionally, there is no need to provide a context since Hilt provides @ApplicationContext
and @ActivityContext
annotations to get different contexts. Any injected context in this sample app is annotated with @ApplicationContext
.
Remember to include
AndroidInjectionModule
with one of your modules in order to be able to inject the activities and fragments without Hilt.
The last thing before making a sanity check is to tell Hilt how to inject AndroidInjector
which is used in AndroidInjectionModule
with using an entry point.
An entry point is a bridge between Hilt and the objects which cannot get its dependencies by Hilt.
Checkpoint: If you run the app, everything should be working fine. You can check this issue if you see any error similar to “Expected @HiltAndroidApp to have a value. Did you forget to apply the Gradle Plugin?”.
Activities & Fragments
Hilt provides @AndroidEntryPoint
annotation which can also be used for activities and fragments. When migrating activities and fragments, all needs to be done is to add @AndroidEntryPoint
annotation and remove the boilerplate code for dagger injection with HasAndroidInjector
. Since this app is using DaggerAppCompatActivity
and DaggerFragment
, replacing them with AppCompatActivity
and Fragment
is sufficient.
Now, Dagger modules ActivityBuilderModule
,FragmentBuilderModule
and AndroidInjectionModule
can be removed. Also, DaggerApplication
can be replaced by Application
, since there is no need for AndroidInjection
. You can see these changes from this commit.
Checkpoint: Run the app and everything should be working fine.
Note: Currently, Hilt does not support Kotlin default args. You can check the issue from here.
ViewModels
In order to inject a ViewModel with Hilt, Jetpack integrations for ViewModel should be added to the project. This library provides @ViewModelInject
annotation which is used for ViewModel constructor injection.
viewModels()
extension could be used in an activity or a fragment to get a ViewModel injected using ViewModelInject
. Use activityViewModels()
extension to scope the ViewModel to an activity in a fragment.
After applying these changes to all ViewModels, ViewModelModule
and ViewModelProviderFactory
could be removed.
Checkpoint: Run the app and everything should be working fine.
Note 1: If the ViewModel is scoped to a navigation graph,
defaultViewModelProviderFactory
could be used as a factory.Note 2:
SavedStateHandle
requires to be annotated with@Assisted
when it is injected to a ViewModel.
WorkManager
Injecting WorkManager also requires Jetpack integrations.
@WorkerInject
annotation is used to inject a worker.
In order to inject the worker factory to the graph, Jetpack integrations provideHiltWorkerFactory
which could be used the setup the work manager configurations in the application class.
Note: Do not forget to remove the default
WorkManager
initialization in the Manifest.
Testing
The sample app uses dagger for integration tests. TestCoroutineModule
is used in TestAppComponent
to replace the CoroutineModule
used in production. Also, CustomTestRunner
is used to override the application with LiveDataWithFlowTestApplication
. Let’s start migrating tests to Hilt with adding Hilt dependencies to the gradle file.
LiveDataWithFlowTestApplication
could be removed since Hilt provides HiltTestApplication
.
In order to remove TestAppComponent
and use ApplicationComponent
by Hilt, TestCoroutineModule
should be installed in ApplicationComponent
.
Bear in mind that there are 2 coroutine modules installed in ApplicationComponent
now. Luckily, Hilt can uninstall modules too with @UninstallModules
annotation.
Moreover, every test needs to be annotated with @HiltAndroidTest
and HiltAndroidRule
should be used in these tests.
If there is more than one rule, then HiltAndroidRule
needs to be executed in the first order. You can check how to give an order to the test rules from docs.
Note: Using
launchFragmentInContainer
is currently not possible with Hilt since it uses a hardcoded activity that does not have a Hilt annotation. You can check the workaround from here.
Checkpoint: If you run the tests, everything should be working fine.
Even though it takes some time to migrate Dagger to Hilt, it is a powerful library and makes developers' life easier by removing lots of boilerplate codes. It also simplifies the Dagger’s complexity with its handy annotations. In short, you won’t regret it after migrating to Hilt!