HILT — Dependency injection new library
By: Karishma Agrawal
Tags: New In Android, Dependency Injection, Dagger Hilt, Android App Development, AndroidDev

Dependency injection plays a vital role in good app architecture. Why? I have answered this question in my previous article.

Hilt provides a standard way to use DI in your application. It provides containers for every Android class in your project and automatically manages their life cycles.

Hilt is built on top of the popular DI library Dagger in order to enhance the compile-time correctness, runtime performance, scalability, and Android Studio support that Dagger provides.

Almost 75% of the applications on Play store use Dagger. Hilt was created on top of Dagger to make migration easier.

Since many Android framework classes are instantiated by the OS itself, using Dagger in Android apps would mean having an associated boilerplate. Unlike Dagger, Hilt is integrated with Jetpack libraries and Android framework classes which removes most of the boilerplate. This means one can focus on the important parts of defining and injecting bindings without worrying about managing all the setup and wiring of Dagger.

It automatically generates and provides:

Components for integrating Android framework classes with Dagger that you would otherwise need to create by hand.

with Dagger that you would otherwise need to create by hand. Scope annotations for the components that Hilt generates automatically.

for the components that Hilt generates automatically. Predefined bindings and qualifiers.

Hilt in your project

Adding dependencies:

Add plugin to your project build.gradle file:

buildscript {

...

dependencies {

...

classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'

}

}

2. Add these dependencies in your app build.gradle file

...

apply plugin: 'kotlin-kapt'

apply plugin: 'dagger.hilt.android.plugin'



android {

...

}



dependencies {

implementation "com.google.dagger:hilt-android:2.28-alpha"

kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"

}

Hilt in your application

Just as with manual dependency, here we create dependencies container class to get dependencies. These dependencies are used across the whole application, they need to be placed in such a way that all activities can use them. In the application class.

The Application class needs to be annotated with @HiltAndroidApp to add a container that is attached to the app’s life cycle. Open application class and add the annotation to it:

@HiltAndroidApp

class LogApplication : Application() {

...

}

The application container is the parent container of the app, which means that other containers can access the dependencies that it provides.

By following steps we ensure our app is ready to use Hilt.

Inject dependencies into Android classes

@AndroidEntryPoint

Hilt can provide dependencies to other Android classes using this annotation:

@AndroidEntryPoint

class ExampleActivity : AppCompatActivity() { ... }

Hilt currently supports the following Android classes:

Application (by using @HiltAndroidApp )

(by using ) Activity

Fragment

View

Service

BroadcastReceiver

Hilt only supports activities that extend FragmentActivity (like AppCompatActivity ) and fragments that extend the Jetpack library Fragment , not the (now deprecated) Fragment from the Android platform. Hilt does not support retained fragments.

If you annotate an Android class with @AndroidEntryPoint , then you also must annotate Android classes that depend on it.

@Inject

we use this annotation to obtain dependencies from a component.

@AndroidEntryPoint

class ExampleActivity : AppCompatActivity() {



@Inject lateinit var analytics: AnalyticsAdapter

...

}

Note: Fields injected by Hilt cannot be private. Attempting to inject a private field with Hilt results in a compilation error.

One way to provide binding information to Hilt is constructor injection. Use this annotation on the constructor of a class to tell Hilt how to provide instances of that class:

class AnalyticsAdapter @Inject constructor(

private val service: AnalyticsService

) { ... }



@ Module

Sometimes a type cannot be constructor-injected. This can happen for multiple reasons. For example, you cannot constructor-inject an interface. You also cannot constructor-inject a type that you do not own, such as a class from an external library. In these cases, you can provide Hilt with binding information by using Hilt modules.

This annotation helps you to provide instances of certain types.

Sometimes a type cannot be constructor-injected. This can happen for multiple reasons. For example, you cannot constructor-inject an interface. You also cannot constructor-inject a type that you do not own, such as a class from an external library. In these cases, you can provide Hilt with binding information by using Hilt modules. This annotation helps you to provide instances of certain types. @Binds

This annotation tells Hilt which implementation to use when it needs to provide an instance of an interface.

The annotated function provides the following information to Hilt:

1. The function return type tells Hilt what interface the function provides instances of.

2. The function parameter tells Hilt which implementation to provide.

interface AnalyticsService {

fun analyticsMethods()

}



// Constructor-injected, because Hilt needs to know how to

// provide instances of AnalyticsServiceImpl, too.

class AnalyticsServiceImpl @Inject constructor(

...

) : AnalyticsService { ... }



@Module

@InstallIn(ActivityComponent::class)

abstract class AnalyticsModule {



@Binds

abstract fun bindAnalyticsService(

analyticsServiceImpl: AnalyticsServiceImpl

): AnalyticsService

}

@Provides

It’s mostly used to provide third-party library instances.(classes like Retrofit, OkHttpClient , or Room databases)

@Module

@InstallIn(ActivityComponent::class)

object AnalyticsModule {



@Provides

fun provideAnalyticsService(

// Potential dependencies of this type

): AnalyticsService {

return Retrofit.Builder()

.baseUrl("https://example.com")

.build()

.create(AnalyticsService::class.java)

}

}

@Qualifier

A qualifier is an annotation that you use to identify a specific binding for a type when that type has multiple bindings defined.

package com.example.android.hilt.di



@Qualifier

annotation class InMemoryLogger



@Qualifier

annotation class DatabaseLogger

@InstallIn

The Hilt module class should also be annotated with @InstallIn to specify the scope of the module. For example, if we annotate the module with @InstallIn(ActivityComponent::class) , then the module will bind to the activity lifecycle.

Hilt provides a few more components you can use to bind dependencies to Android classes — have a look:

Screenshot from android developer official website

The application context binding is also available using @ApplicationContext .