💉Dependency Injection with Dagger Hilt


Dependency Injection (DI) is a design pattern that helps make Android apps more maintainable, modular, and testable. It reduces tight coupling between classes by delegating the responsibility of providing dependencies to a centralized system.
🔧 Adding Hilt to Your Project
1. Project-Level build.gradle.kts
Add the Hilt plugin declaration:
plugins {
id("com.google.dagger.hilt.android") version "2.56.1" apply false //replace with current version
}
2. App-Level build.gradle.kts
Enable Hilt and KSP in the app module:
plugins {
id("com.google.devtools.ksp")
id("com.google.dagger.hilt.android")
}
dependencies {
implementation("com.google.dagger:hilt-android:2.56.1") //replace with current version
ksp("com.google.dagger:hilt-android-compiler:2.56.1") //replace with current version
}
🚀 Initialize Hilt in the Application Class
Create a class that extends Application
and annotate it with @HiltAndroidApp
. This class serves as the entry point for setting up Hilt’s dependency container.
@HiltAndroidApp
class BaseApp : Application()
Also, register this class in your AndroidManifest.xml
:
<application
android:name=".BaseApp"
... >
</application>
📥 Injecting Dependencies in Activities
To enable injection in an Activity, annotate it with @AndroidEntryPoint
.
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
// Dependencies will be injected here
}
🧃 Example: Injecting a Custom Class
Suppose you have a simple class like this:
class JuiceMaker(private val flavor: String) {
fun makeJuice(): String = "Here's your $flavor juice! 🧃"
}
To inject this with Hilt, create a module that provides it:
@Module
@InstallIn(SingletonComponent::class)
object MyModule {
@Provides
@Singleton
fun provideFlavor(): String = "mango"
@Provides
@Singleton
fun provideJuiceMaker(flavor: String): JuiceMaker {
return JuiceMaker(flavor)
}
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
SingletonComponent
ensures these dependencies live throughout the app's lifecycle.
🧩 Using Injected Dependencies
Now you can inject and use them directly in MainActivity
:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject lateinit var juiceMaker: JuiceMaker
@Inject lateinit var retrofit: Retrofit
@Inject lateinit var flavor: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Column {
Text(juiceMaker.makeJuice())
Text(retrofit.baseUrl())
Text("Flavor: $flavor")
}
}
}
}
⚖️ Without DI vs With DI (Hilt)
Feature | ❌ Without DI | ✅ With Hilt (DI) |
Object Creation | Manual | Automatic via @Inject and @Provides |
Reusability | Difficult | Easily reusable across classes |
Testability | Hard to mock | Can be replaced with mocks in tests |
Scalability | Becomes tightly coupled | Modular and clean architecture |
Lifecycle Awareness | Manual lifecycle management | Lifecycle-aware scoping (@Singleton , etc.) |
Flexibility | Constructor changes require refactor | Hilt handles constructor injection updates |
✅ Conclusion
Using Hilt for Dependency Injection helps simplify app architecture by:
Automating object creation and management
Promoting loose coupling and separation of concerns
Enhancing testability and maintainability
Subscribe to my newsletter
Read articles from Sagnik Mukherjee directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Sagnik Mukherjee
Sagnik Mukherjee
Native Android Developer and content creator