top of page

Quick start guide on Hilt and Dependency Injection in Kotlin Android apps

Updated: Aug 24, 2023


Quick start guide on Hilt and Dependency Injection in Kotlin Android apps

Dependency injection is an essential architectural pattern in Android app development that allows us to manage and provide dependencies to classes or components in a flexible and scalable way. Traditionally, setting up dependency injection in Android apps involved writing a significant amount of boilerplate code. However, with the introduction of Hilt, a dependency injection library from Google built on top of Dagger, this process has become much more streamlined and intuitive.


In this blog, we will explore the step-by-step process of integrating Hilt into a Kotlin Android app and leverage its power to manage dependencies effortlessly.


What is Hilt?


Hilt is a dependency injection library for Android, developed by Google. It is designed to simplify the implementation of dependency injection in Android apps by reducing boilerplate code and providing a set of predefined components and annotations.


Hilt is built on top of Dagger, which is a popular dependency injection framework for Java and Android. By using Hilt, developers can focus more on writing clean and modular code, and Hilt takes care of generating the necessary Dagger code under the hood.


Prerequisites


Before we proceed, make sure you have the following set up in your development environment:

  1. Android Studio with the latest Kotlin plugin.

  2. A Kotlin-based Android project.


Integrating Hilt with Kotlin Android app


Step 1: Add Hilt Dependencies


The first step is to include the necessary Hilt dependencies in your project.


Open your app's build.gradle file and add the following lines:

dependencies {
    implementation "com.google.dagger:hilt-android:2.41"
    kapt "com.google.dagger:hilt-android-compiler:2.41"
}

Hilt requires two dependencies - hilt-android for the runtime library and hilt-android-compiler for annotation processing during build time.


Step 2: Enable Hilt in the Application Class


Next, we need to enable Hilt in the Application class of our app. If you don't already have an Application class, create one by extending the Application class. Then, annotate the Application class with @HiltAndroidApp, which informs Hilt that this class will be the entry point for dependency injection in our app:

@HiltAndroidApp
class MyApp : Application() {
    // ...
}

The @HiltAndroidApp annotation generates the necessary Dagger components and modules under the hood, and it also initializes Hilt in the Application class.


Step 3: Setting up Hilt Modules


Hilt uses modules to provide dependencies. A module is a class annotated with @Module, and it contains methods annotated with @Provides. These methods define how to create and provide instances of different classes. Let's create an example module that provides a singleton instance of a network service:

@Module
@InstallIn(ApplicationComponent::class)
object NetworkModule {
    @Singleton
    @Provides
    fun provideNetworkService(): NetworkService {
        return NetworkService()
    }
}

In this example, we define a method provideNetworkService() annotated with @Provides that returns a NetworkService instance. The @Singleton annotation ensures that the same instance of NetworkService is reused whenever it is requested.


Step 4: Injecting Dependencies


After setting up the module, we can now use the @Inject annotation to request dependencies in our Android components, such as activities, fragments, or view models. For example, to inject the NetworkService into a ViewModel, annotate the View Model with @HiltViewModel.

@HiltViewModel
class MyViewModel @Inject constructor(
    private val networkService: NetworkService
) : ViewModel() {
    // ...
}

In this example, the MyViewModel class requests the NetworkService dependency via constructor injection. Hilt will automatically provide the required NetworkService instance when creating MyViewModel.


Step 5: AndroidEntryPoint Annotation


To enable dependency injection in activities and fragments, annotate them with @AndroidEntryPoint:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var networkService: NetworkService

    // ...
}

By using the @AndroidEntryPoint annotation, we tell Hilt to inject dependencies into this activity. Here, we inject the NetworkService instance into the networkService variable using field injection. After injecting, the networkService variable will be ready to use within the MainActivity.


Step 6: Gradle Plugin Configuration


To ensure smooth integration and prevent certain issues, we need to configure the Gradle plugin. Add the following configurations to your app's build.gradle file:

android {
    // ...
    defaultConfig {
        // ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments["dagger.hilt.android.internal.disableAndroidSuperclassValidation"] = "true"
            }
        }
    }
    // ...
}

With this configuration, we disable certain superclass validation checks that can interfere with Hilt's code generation and avoid potential runtime issues.


Usage and Benefits of Hilt

  1. Simplified Dependency Injection: Hilt significantly reduces the boilerplate code required for dependency injection. The use of annotations allows developers to declare dependencies clearly and concisely.

  2. Scoping and Caching: Hilt provides built-in support for scoping annotations like @Singleton, @ActivityScoped, @FragmentScoped, etc., ensuring that singleton instances are cached and reused when requested. This saves memory and processing time.

  3. Easy Testing: Hilt simplifies testing by allowing you to swap out dependencies easily using different modules for testing, providing clear separation between production and test code.

  4. Seamless Integration with Android Components: Hilt seamlessly integrates with Android activities, fragments, services, and view models, making it convenient to inject dependencies into these components. It allows for smooth development without worrying about manual instantiation or passing dependencies around.


Conclusion


In this blog, we explored the step-by-step process of integrating Hilt into a Kotlin Android app. We started with a brief introduction to Hilt and its benefits. Then, we walked through the integration process, including adding dependencies, enabling Hilt in the Application class, setting up Hilt modules, injecting dependencies into Android components, and configuring the Gradle plugin. Hilt significantly simplifies the dependency injection process, resulting in a cleaner and more maintainable codebase.


By leveraging Hilt's power, developers can enhance the modularity and testability of their Android apps, leading to a smoother development process and a better user experience.


Happy coding!

Blog for Mobile App Developers, Testers and App Owners

 

This blog is from Finotes Team. Finotes is a lightweight mobile APM and bug detection tool for iOS and Android apps.

In this blog we talk about iOS and Android app development technologies, languages and frameworks like Java, Kotlin, Swift, Objective-C, Dart and Flutter that are used to build mobile apps. Read articles from Finotes team about good programming and software engineering practices, testing and QA practices, performance issues and bugs, concepts and techniques. 

bottom of page