0
0
Android Kotlinmobile~15 mins

Dependency injection with Hilt in Android Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - Dependency injection with Hilt
What is it?
Dependency injection with Hilt is a way to give parts of your Android app the things they need to work, like tools or data, without making them find or create those things themselves. Hilt is a library that helps manage this automatically, so your code stays clean and easy to change. It makes sure each part gets exactly what it needs, when it needs it, without extra effort.
Why it matters
Without dependency injection, your app parts would have to create or find their own tools, which makes the code messy and hard to change. Hilt solves this by organizing how parts get their tools, making apps easier to build, test, and update. This means faster development and fewer bugs, which you can feel as smoother app updates and better app quality.
Where it fits
Before learning Hilt, you should understand basic Kotlin programming and how Android apps are structured with Activities and ViewModels. After mastering Hilt, you can explore advanced topics like custom scopes, multi-module projects, and testing with mock dependencies.
Mental Model
Core Idea
Dependency injection with Hilt is like a smart helper that automatically gives each part of your app the exact tools it needs, so those parts don’t have to find or build the tools themselves.
Think of it like...
Imagine a kitchen where each chef needs specific ingredients and utensils. Instead of each chef running around to find what they need, a kitchen assistant (Hilt) brings the right items to each chef exactly when they start cooking.
┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│  App Part   │◄─────│  Hilt Helper│─────►│ Dependencies│
└─────────────┘      └─────────────┘      └─────────────┘

Hilt Helper connects app parts to their dependencies automatically.
Build-Up - 7 Steps
1
FoundationWhat is Dependency Injection
🤔
Concept: Dependency injection means giving an object the things it needs instead of letting it create them itself.
In Android, many parts like Activities or ViewModels need other objects to work, such as data sources or network clients. Dependency injection is a way to provide these needed objects from outside, making the code cleaner and easier to test.
Result
You understand that dependency injection separates the creation of objects from their use, improving code organization.
Understanding this separation helps you write code that is easier to maintain and test.
2
FoundationIntroducing Hilt for Android
🤔
Concept: Hilt is a library that helps Android apps use dependency injection easily and safely.
Hilt builds on another tool called Dagger but simplifies setup by providing standard components and annotations. It automatically creates and provides dependencies to Android classes like Activities and ViewModels.
Result
You see how Hilt reduces the amount of code needed to manage dependencies and integrates well with Android lifecycle.
Knowing Hilt’s role makes it clear how it saves time and reduces errors compared to manual dependency management.
3
IntermediateUsing @Inject to Request Dependencies
🤔Before reading on: do you think @Inject creates a new object or just tells Hilt what is needed? Commit to your answer.
Concept: @Inject marks where a dependency is needed or how to create it.
You add @Inject to a constructor or a field to tell Hilt: "This class needs this dependency." Hilt then knows to provide that dependency automatically when creating the class.
Result
Classes receive their dependencies without manual creation, making code simpler and clearer.
Understanding @Inject as a request, not creation, helps avoid confusion about object lifecycles.
4
IntermediateProviding Dependencies with @Module and @Provides
🤔Before reading on: do you think Hilt can create all dependencies automatically or do some need manual instructions? Commit to your answer.
Concept: Some dependencies need instructions on how to be created, which you provide using @Module and @Provides annotations.
You create a module class annotated with @Module and add functions annotated with @Provides that tell Hilt how to build complex or external dependencies like Retrofit or databases.
Result
Hilt knows how to create and supply these dependencies wherever needed in the app.
Knowing when and how to provide dependencies manually is key to using Hilt effectively in real apps.
5
IntermediateUnderstanding Scopes and Lifecycles
🤔Before reading on: do you think all dependencies live forever or can their lifetime be limited? Commit to your answer.
Concept: Scopes control how long a dependency lives and when it is reused or recreated.
Hilt supports scopes like @Singleton for app-wide single instances and @ActivityScoped for objects tied to an Activity’s life. This helps manage memory and performance by reusing objects when appropriate.
Result
You can control dependency lifetimes to match app component lifecycles, avoiding leaks or unnecessary recreations.
Understanding scopes prevents common bugs and improves app efficiency.
6
AdvancedInjecting ViewModels with Hilt
🤔Before reading on: do you think ViewModels need special handling for injection or are they like any other class? Commit to your answer.
Concept: Hilt integrates with Android’s ViewModel system to inject dependencies safely and cleanly.
By annotating ViewModels with @HiltViewModel and using @Inject in their constructor, Hilt provides dependencies automatically. You also use the by viewModels() delegate in Activities or Fragments to get the injected ViewModel.
Result
ViewModels receive dependencies without boilerplate code, respecting lifecycle and avoiding memory leaks.
Knowing this integration simplifies state management and improves app architecture.
7
ExpertCustom Scopes and Multi-Module Setup
🤔Before reading on: do you think Hilt’s built-in scopes cover all cases or can you create your own? Commit to your answer.
Concept: Advanced apps may need custom scopes and multi-module dependency setups for better organization and performance.
You can define your own scope annotations to control lifetimes beyond built-in ones. In multi-module projects, you set up Hilt components and modules in each module and connect them properly to share dependencies.
Result
Your app scales well with clear dependency boundaries and efficient resource use.
Understanding custom scopes and modular setup is crucial for large, complex Android apps.
Under the Hood
Hilt generates code at compile time using Dagger’s annotation processor. It creates components that know how to build and provide dependencies based on annotations like @Inject, @Module, and @Provides. When your app runs, Hilt’s generated code creates and caches dependencies according to their scopes and injects them into Android classes automatically.
Why designed this way?
Hilt was designed to simplify Dagger’s complex setup by providing standard components for Android lifecycles and reducing boilerplate. This design balances compile-time safety and runtime performance, avoiding reflection and runtime errors common in other injection methods.
┌───────────────┐
│  Source Code  │
│ (@Inject,     │
│  @Module)     │
└──────┬────────┘
       │ Annotation Processing
       ▼
┌───────────────┐
│ Generated     │
│ Components &  │
│ Factories     │
└──────┬────────┘
       │ Runtime Injection
       ▼
┌───────────────┐
│ Android App   │
│ Classes       │
│ (Activities,  │
│ ViewModels)   │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does @Inject create a new object every time or just declare a need? Commit to your answer.
Common Belief:Many think @Inject creates a new object every time it is used.
Tap to reveal reality
Reality:@Inject only declares that a dependency is needed; Hilt decides when and how to create or reuse the object based on scope.
Why it matters:Misunderstanding this leads to inefficient code or bugs from unexpected object lifetimes.
Quick: Can Hilt inject dependencies into any class automatically? Commit to your answer.
Common Belief:Some believe Hilt can inject dependencies into any class without setup.
Tap to reveal reality
Reality:Hilt only injects into Android classes it supports or classes annotated properly; others need manual injection or factory methods.
Why it matters:Assuming automatic injection everywhere causes runtime crashes or missing dependencies.
Quick: Is it okay to create dependencies manually alongside Hilt? Commit to your answer.
Common Belief:Some think mixing manual creation and Hilt injection is fine and harmless.
Tap to reveal reality
Reality:Mixing manual and injected dependencies can cause inconsistent states and hard-to-find bugs.
Why it matters:This leads to duplicated objects, memory leaks, or unexpected behavior.
Quick: Does using @Singleton mean the object lives forever? Commit to your answer.
Common Belief:Many believe @Singleton means the object never gets destroyed.
Tap to reveal reality
Reality:@Singleton means one instance per Hilt component lifetime, usually the app lifetime, but can be different in tests or custom components.
Why it matters:Wrong assumptions about lifetime cause memory leaks or stale data.
Expert Zone
1
Hilt’s generated components form a hierarchy matching Android lifecycles, allowing fine-grained control over dependency lifetimes.
2
Using qualifiers like @Named or custom annotations lets you provide multiple versions of the same type safely.
3
Hilt supports assisted injection for cases where some parameters must be provided at runtime, blending injection with manual input.
When NOT to use
Hilt is not ideal for very small projects where manual injection is simpler, or for non-Android Kotlin projects. Alternatives include manual injection, Koin for runtime injection, or Dagger without Hilt for more control.
Production Patterns
In production, Hilt is used to inject repositories, network clients, databases, and ViewModels. Teams often combine Hilt with modularization, custom scopes, and testing frameworks to build scalable, maintainable apps.
Connections
Inversion of Control (IoC)
Dependency injection is a form of IoC where control of creating dependencies is inverted from the class to an external system.
Understanding IoC helps grasp why dependency injection improves modularity and testability.
Service Locator Pattern
Both provide dependencies to classes, but service locator requires classes to ask for dependencies, while injection provides them automatically.
Knowing the difference clarifies why dependency injection leads to cleaner, more testable code.
Factory Design Pattern
Dependency injection often uses factories behind the scenes to create objects, separating creation logic from use.
Recognizing this connection helps understand how injection frameworks manage complex object creation.
Common Pitfalls
#1Forgetting to annotate Android classes with @AndroidEntryPoint.
Wrong approach:class MainActivity : AppCompatActivity() { @Inject lateinit var repo: UserRepository }
Correct approach:@AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject lateinit var repo: UserRepository }
Root cause:Without @AndroidEntryPoint, Hilt cannot generate the necessary code to inject dependencies into the class.
#2Providing dependencies without proper scope causing multiple instances.
Wrong approach:@Module @InstallIn(SingletonComponent::class) object NetworkModule { @Provides fun provideApi(): ApiService = ApiService() }
Correct approach:@Module @InstallIn(SingletonComponent::class) object NetworkModule { @Provides @Singleton fun provideApi(): ApiService = ApiService() }
Root cause:Missing @Singleton causes Hilt to create a new instance every time, which may waste resources or cause bugs.
#3Injecting dependencies into classes not managed by Hilt.
Wrong approach:class HelperClass { @Inject lateinit var repo: UserRepository }
Correct approach:class HelperClass @Inject constructor(private val repo: UserRepository) { // Use repo here }
Root cause:Hilt can only inject into classes it creates or Android classes annotated properly; manual creation requires constructor injection.
Key Takeaways
Dependency injection with Hilt automates providing needed objects to Android app parts, improving code clarity and testability.
Hilt uses annotations like @Inject, @Module, and @Provides to know what to inject and how to create dependencies.
Scopes control how long dependencies live, helping manage resources and app performance.
Hilt integrates tightly with Android lifecycles, especially for Activities and ViewModels, reducing boilerplate code.
Understanding Hilt’s generated code and scopes helps avoid common mistakes and build scalable, maintainable apps.