DEV Community

RockAndNull
RockAndNull

Posted on • Originally published at rockandnull.com on

Hilt: the shortest guide for the DI framework for Anroid

Hilt: the shortest guide for the DI framework for Anroid

I have been looking for a robust-yet-simple dependency injection framework (DI) for a while. The last time I looked around, I concluded that Koin was the DI framework that met those requirements (according to my needs of course).

In my opinion, Dagger is the de-facto DI framework in Android, powerful and popular, but you wouldn't call it simple. Then Hilt came around. Which hits the sweet spot. It's pretty much Dagger (with all its speed and compile-time safety) but facelifted into something fast and easy.

I will try to give a super quick overview of how to use Hilt in your project. I did it in one of my existing projects, and I was delighted by how quickly I could get started.

Set up

Hilt is easy to use because it performs a lot of "magic" (i.e. code generation) behind the scenes. For this to happen, you need to add its annotation processors and other dependencies to your build.gradle files.

Reference the official doc since you will need quite a lot of additions.

Dependencies

To make a class injectable, just annotate the constructor with @Inject.

class MyHelper @Inject constructor() { 
    [...] 
}
Enter fullscreen mode Exit fullscreen mode

Every time this is injected, a new instance will be created. To inject the same single instance (that will be created the first time) annotate with @Singleton.

@Singleton
class MyHelper @Inject constructor() { 
    [...] 
}
Enter fullscreen mode Exit fullscreen mode

Scoping for views/fragments/activities/view-models are available as well (doc). This means that when injections are requested, the same instance will be returned if in the same component. For instance, the following class will be created every time it's requested from a new Fragment.

@FragmentScoped
class MyHelper @Inject constructor() { 
    [...] 
}
Enter fullscreen mode Exit fullscreen mode

Modules

If you have an interface and an implementation, then you will need a module. Think of these as instructions on how to instantiate a class. For simple interface/implementation use @Binds.

@InstallIn(ApplicationComponent::class)
abstract class MyHelperModule {
  @Binds
  abstract fun bindMyHelper(myHelperImpl: MyHelperImpl): MyHelper
}
Enter fullscreen mode Exit fullscreen mode

For more complicated "instructions" use @Provides. Context is provided by default if you annotate with @ApplicationContext or @ActivityContext.

@Module
@InstallIn(ApplicationComponent::class)
object MyHelperModule {

  @Provides
  fun provideMyHelper(
    @ApplicationContext context: Context
  ) {
      [...]
      return MyHelper(context)
  }
}
Enter fullscreen mode Exit fullscreen mode

Where you @InstallIn along with the scoping annotation we saw above, defines when a new instance of your class will be created and provided. For instance, this will create a new instance of MyHelper for each Activity that requests this.

@Module
@InstallIn(ActivityComponent::class)
abstract class MyHelperModule {

  @ActivityScoped
  @Binds
  abstract fun bindMyHelper(myHelper: MyHelperImpl): MyHelper
}
Enter fullscreen mode Exit fullscreen mode

Dependencies injection

Now you defined what are your dependencies. It's time to inject and use them.

For your classes that you can instantiate, use constructor injection using @Inject (like we saw above).

class MyHelper @Inject constructor(private val toBeInjected: AnotherHelper) { 
    [...] 
}
Enter fullscreen mode Exit fullscreen mode

For system classes that are not instantiated by you (e.g. Activities, Fragments, Services), use members injections using @AndroidEntryPoint, @Inject, and lateinit.

@AndroidEntryPoint
class MyActivity : AppCompatActivity() {

  @Inject lateinit var myHelper: MyHelper
  [...]
}
Enter fullscreen mode Exit fullscreen mode

ViewModels

To inject Jetpack's ViewModels you will need something different.

First, install these additional dependencies.

Then use @ViewModelInject (instead of plain @Inject) on the constructor. To inject a SavedStateHandle, annotate with @Assisted. Finally, no need for any ViewModel factory, use by viewModels() to retrieve your ViewModel in your Fragment/Activity.

class MyViewModel @ViewModelInject constructor(
  @Assisted private val savedState: SavedStateHandle,
  private val myHelper: MyHelper,
) : ViewModel() {
  [...]
}
Enter fullscreen mode Exit fullscreen mode

Hopefully, in this short post, I gave you a glimpse of Hilt and how to use it. Deep dive into the doc for more use cases. Happy coding!

Top comments (0)