How to use Hilt for dependency injection in your Android projects

Difficulty: Intermediate

What is Dependency Injection?

dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on.[1] But what is a dependency injection library? dependency injection libraries like Dagger 2 or Hilt, are used in projects to reduce boilerplate code and have all the benifits of DI with least amout of complexity, think of Hilt as someone helping you prepare dinner you tell him how many dishes you need for each type ex: 1 soup for each person and 1 bowl of salad for everyone. Now Hilt will cook the soup and prepare the salad, and just by telling him when and how he can take care of everything.

Hilt library works in a very similar way you just tell Hilt how to make an instance of your class and for how long would you like to keep that instance and it will be provided whenever you inject it somewhere!

For this example i am going to use the project from my article Making API request using Ktor client in Android as a starter project for this tutorial please bare in mind that this tutorial is not intended for complete beginners so take your time in looking at my previous tutorials and come back when you feel confident enough.

Declaring dependencies

First add the `hilt-android-gradle-plugin` to your projects build.gradle file, latest version.

plugins {
  ...
  id 'com.google.dagger.hilt.android' version '2.44' apply false
}

Then apply the plugin and add these dependencies in your module’s build.gradle file

...
plugins {
  id 'kotlin-kapt'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

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

// Allow references to generated code
kapt {
  correctErrorTypes true
}

Setting up the application class

In order for Hilt to work correctly we need to create an application class and annotate it with `@HiltAndroidApp` and include it in our Manifest.xml file in the android:name attribute, create the application class (for example: “MyKtorClientApplication”) in the root directory of your package name.

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class MyKtorClientApplication: Application() {
}
    <application
        android:name=".MyKtorClientApplication"
..................

Teaching Hilt how to cook!

Create a new package named di (dependency injection) create a new Kotlin object in this package called “AppModule”, we will annotate this object with @Module and @InstallIn(). in the install in parenthesis we will specify the scope we would like to use for this module, in other words for how long should our instances live.

In this example we will use SingletonComponent, a list of all components with details can be found here.

it is worth mentioning that setting the scope of the Kotlin object like this doesn’t mean the instances declared in this file will have this scope, we will have to scope each function individually.

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
}

In this file we want to teach Hilt how to make a Ktor instance

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import io.ktor.client.*
import io.ktor.client.engine.android.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
import javax.inject.Singleton

/**
 * Created by Taha Ben Ashur (https://github.com/tahaak67) on 11,Feb,2023
 */
@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides //tells dagger hilt this is a recipe ;)
    @Singleton //only one instance will be shared between our viewModels
    fun provideKtorClient(): HttpClient {
        val client = HttpClient(Android) {
            expectSuccess = true
            defaultRequest {
                contentType(ContentType.Application.Json)
                accept(ContentType.Application.Json)
            }
            install(ContentNegotiation) {
                json(Json {
                    ignoreUnknownKeys = true
                })
            }
            install(Logging) {
                level = LogLevel.ALL
            }
        }
        return client
    }
    
    @Provides
    @Singleton
    fun providePostApi(httpClient: HttpClient):PostApi {
        return PostApiImpl(httpClient)
    }
}

Asking Hilt to provide the dependencies for us.

Switch over to our view model, here we need annotate the view model class with @HiltViewModel and the constructor with @Inject constructor (), injecting the dependencies like this is called “Constructor Injection”, and off course remove the line were we create an instance of PostApi manually as we won’t need that anymore.

@HiltViewModel
class PostsFeedViewModel @Inject constructor(private val postApi: PostApi) : ViewModel() {
        ......

Lastly annotate both the main activity and the PostsFeedFragment with @AndroidEntryPoint, this will allow Hilt to provide dependencies to the PostsFeedFragment’s view model and since our fragment depends on MainActivity we must also annotate it.

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
....
}

And like that you can also apply the same to AddPostViewModel and AddPostFragment, but before you do that you can go ahead and try to run your app, it should work normally and display the list of posts.

Field Injection

before i end this post i would like to show an example of doing field injection in an activity, since we use Coil to load images from the internet lets make a single image loader instance and share it throughout our app, use the Coil docs to learn how to create an image loader instance and inject it in the fragment like this.

    @Inject lateinit var imageLoader: ImageLoader
0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments