Use Dependency Injection with Hilt

An insatiably curious mind, I love exploring, assembling, and seasoning my ideas, whether in code or in the kitchen.
Hi Kooktliners 👋
A solid application always needs strong foundations. Today, let’s talk about something essential : Dependency Injection (DI). Sounds weird, maybe even illicit ? Don’t worry, it’s perfectly legal. 😏
So, what is DI ?
DI is all about providing the dependencies of a class from the outside. Let’s take a simple and tastyyy analogy here:
You‘re a cook (obvious right 🤌🏾), and you’re preparing a homemade burger. So you buy a burger buns to the bakery, the meat to the butcher shop, the lettuce and tomato to the grocery. You go to the bakery for the buns, the butcher for the meat, and the grocery store for the veggies. You know the recipe, but you’re responsible for gathering all the ingredients yourself.
Now imagine this :
With Dependency Injection, you don't have to fetch the ingredients yourself. You have a provider that delivers top-quality products right to your kitchen 🤤. And if you don’t like one of them? You can swap it out easily. That’s the power of DI, it lets you focus on your recipe, not the ingredients.
With DI you don’t need to buy the ingredients by yourself, you have a provider who delivers you the best products and if you don’t like them; you can change them. That is the power of injection, you focus only on your recipe not the ingredients.
How can I use it ?
For Android, we use Hilt, a DI library that simplifies the setup and removes most of the boilerplate code.
Before getting started, make sure to set up the necessary ustensiles :
In your top-level build-grade file, apply the KSP and Hilt plugins.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
...
//Hilt
alias(libs.plugins.ksp) apply false
alias(libs.plugins.hilt) apply false
}
In your module-level file add the following :
plugins {
...
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)
id("com.google.protobuf") version "0.9.5" // Protobuf
}
...
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.24.3"
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
create("java") {
option("lite")
}
}
}
}
}
dependencies {
...
// DataStore preference
implementation("androidx.datastore:datastore-preferences:1.1.7")
// Hilt
implementation("com.google.dagger:hilt-android:2.52")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
implementation("androidx.hilt:hilt-work:1.2.0")
ksp("com.google.dagger:hilt-compiler:2.52")
// Protobuf
implementation("com.google.protobuf:protobuf-javalite:3.24.3")
implementation("androidx.datastore:datastore:1.1.7")
}
Here’s your 5-steps recipe:
Create a file in the root package
Create your Application file in “app/(kotlin|java)/your/company/package/YourApplicationNameApplication.kt” :
app/ ├── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── yourcompany/ │ │ └── yourapp/ │ │ └── YourApplicationNameApplication.kt │ └── res/ │ └── ... ├── build.gradle └── ...YourApplicationNameApplication.kt :
@HiltAndroidApp class YourApplicationNameApplication : Application() { }Add your Application class to the AndroidManifest.xml
<application android:name=".YourApplicationNameApplication" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" ... > </application>Establish an entry point in your MainActivity with @AndroidEntryPoint
Now that your Application class is set up, it’s time to tell Hilt where to start injecting things.
To do that, you need to annotate your MainActivity (or any activity where you want to inject dependencies) with the @AndroidEntryPoint annotation.
Here’s how it looks:
import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : AppCompatActivity() { // Your code here }This makes MainActivity a valid entry point for Hilt. Without this annotation, Hilt won’t know it’s allowed to inject anything into this activity, so don’t skip it!
Create your Module file
Now it’s time to tell Hilt how to create the objects (dependencies) your app needs. This is done in a Module, a Kotlin file where you define how to provide certain instances.
Think of it as your kitchen assistant's cheat sheet: you write down how to prepare each ingredient, and they handle the rest.
Here’s an example of a basic module:
import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun provideBurgerService(): BurgerService { return BurgerServiceImpl() } @Provides fun provideTomato(): Tomato { return Tomato(fresh = true) } }A few things to note:
@Module : tells Hilt this file contains dependency instructions.
@InstallIn(SingletonComponent::class) : defines the scope (how long the object lives).
@Provides : tells Hilt how to create the object when needed.
Now when you need one of these dependencies (like BurgerService) in your activity or class, just use @Inject, and voilà!
Annotate your ViewModel with @HiltViewModel
When using ViewModels, you also need to let Hilt know that it should handle the injection for you.
To do that:
Annotate your ViewModel class with @HiltViewModel.
Use @Inject on the constructor to specify the dependencies it needs.
Here’s an example:
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class BurgerViewModel @Inject constructor(
private val burgerService: BurgerService
) : ViewModel() {
fun cookBurger() {
burgerService.makeBurger()
}
}
And in your Activity or Fragment, use the: by viewModels() delegate to get your ViewModel:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: BurgerViewModel by viewModels()
// Now you can use viewModel.cookBurger()
}
And that’s it!
I hope this was useful! And Happy Kooktding !
⚠️ Want to see this in action ? Check out my DataStore article, I show how to use Dependency Injection there too ! [Link to article]
