ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Photo by Andrew Neel on Unsplash

Hilt Migration Guide

Suleyman Fatih Giris
ProAndroidDev
Published in
4 min readAug 14, 2020

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.

build.gradle (Project)
build.gradle (app)

Application Class

The first thing you will do is to add@HiltAndroidApp annotation to the Application class.

Adding @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).

ActivityBuilderModule annotated with @InstallIn

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.

Using @ApplicationContext annotation

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.

Adding an entry point for AndroidInjector

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.

Adding @AndroidEntryPoint to an activity and removing DaggerAppCompatActivity
Adding @AndroidEntryPoint to a fragment and removing DaggerFragment

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.

build.gradle (app)
Injecting a ViewModel with @ViewModelInject

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.

Using viewModels() extension to get a ViewModel

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.

build.gradle (app)

@WorkerInject annotation is used to inject a worker.

Using @WorkerInject to inject a coroutine 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.

Setting up the worker configurations with HiltWorkerFactory

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.

Adding Hilt dependencies for integration tests

LiveDataWithFlowTestApplication could be removed since Hilt provides HiltTestApplication.

Replacing LiveDataWithFlowTestApplication with HiltTestApplication in CustomTestRunner

In order to remove TestAppComponent and use ApplicationComponent by Hilt, TestCoroutineModule should be installed in ApplicationComponent.

Installing TestCoroutineModule 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.

Uninstalling CoroutineModule & Using @HiltAndroidTest and HiltAndroidRule

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: UsinglaunchFragmentInContainer 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!

No responses yet

Write a response