Overview
TecMeli uses Hilt, Google’s recommended dependency injection library for Android, built on top of Dagger. Hilt provides a standardized way to inject dependencies throughout the app, reducing boilerplate and improving testability.Why Dependency Injection?
Testability
Dependencies can be easily mocked or replaced in tests
Loose Coupling
Classes don’t create their dependencies, making code more flexible
Reusability
Dependencies are configured once and reused across the app
Lifecycle Management
Hilt manages object lifecycles (Singleton, ViewModel-scoped, etc.)
Hilt Setup
Application Class
The entry point for Hilt is the Application class annotated with@HiltAndroidApp.
@HiltAndroidApp must be added to your Application class. This annotation triggers Hilt’s code generation and allows Hilt to provide dependencies throughout the app.Activity Injection
Hilt Modules
Hilt modules define how to provide dependencies. TecMeli has two main modules incore/di/:
1. NetworkModule
Location:core/di/NetworkModule.kt
Purpose: Provides network-related dependencies (OkHttp, Retrofit, API interfaces).
@Module
@Module
Tells Hilt this object provides dependencies.
@InstallIn(SingletonComponent::class)
@InstallIn(SingletonComponent::class)
Dependencies live for the entire app lifecycle. Other options:
ActivityComponent: Lives as long as the ActivityViewModelComponent: Lives as long as the ViewModelFragmentComponent: Lives as long as the Fragment
@Provides
@Provides
Marks a function that provides a dependency. Hilt will call this function when the dependency is needed.
@Singleton
@Singleton
Ensures only one instance exists app-wide. The same instance is reused.
Dependency Chain
Dependency Chain
provideOkHttpClient needs AuthInterceptor, Hilt will:- Look for a
@Providesfunction that returnsAuthInterceptor, OR - Look for an
@Injectconstructor inAuthInterceptor
2. RepositoryModule
Location:core/di/RepositoryModule.kt
Purpose: Binds repository interfaces to their implementations.
Abstract Class vs Object
Abstract Class vs Object
RepositoryModule is an abstract class because it uses @Binds, which requires abstract functions.@Binds vs @Provides
@Binds vs @Provides
@Binds is used when:
- The implementation has an
@Injectconstructor - You’re just telling Hilt “use this implementation for this interface”
- More efficient than
@Provides(less generated code)
- You need to configure the object (builder pattern, parameters)
- The class doesn’t have an
@Injectconstructor (third-party libraries)
Constructor Injection
Classes can request dependencies via their constructors using@Inject.
Repository Example
RepositoryModulesays “useProductRepositoryImplforProductRepository”ProductRepositoryImplhas@Inject constructor- Constructor needs
MeliApi - Hilt looks for
MeliApiprovider → findsprovideMeliApi()inNetworkModule provideMeliApi()needsRetrofit- Hilt looks for
Retrofitprovider → findsprovideRetrofit() - Chain continues until all dependencies are resolved
- Hilt creates the object graph and injects everything
Use Case Example
Use cases depend on interfaces (
ProductRepository), not implementations. Hilt provides the implementation bound in RepositoryModule.ViewModel Example
@HiltViewModel
@HiltViewModel
- Tells Hilt this is a ViewModel
- Enables ViewModel injection in Composables via
hiltViewModel() - ViewModels are scoped to Navigation destinations
- Survive configuration changes automatically
Interceptor Example
Dependency Graph
Here’s how dependencies flow in TecMeli: Hilt automatically resolves this entire graph!Scopes
Hilt provides different scopes for different lifecycles.| Scope | Component | Lifetime | Use Case |
|---|---|---|---|
@Singleton | SingletonComponent | App lifetime | Network client, database, repositories |
@ActivityScoped | ActivityComponent | Activity lifetime | Activity-specific services |
@ViewModelScoped | ViewModelComponent | ViewModel lifetime | ViewModel-specific dependencies |
@ActivityRetainedScoped | ActivityRetainedComponent | Activity (survives config changes) | Shared ViewModels |
- Network components:
@Singleton(used throughout the app) - Repositories:
@Singleton(shared across ViewModels) - ViewModels: Automatically scoped to Navigation destinations
Injection in Composables
ViewModel Injection
hiltViewModel() function:
- Provided by
androidx.hilt:hilt-navigation-compose - Injects the ViewModel with all dependencies
- Scopes ViewModel to the Navigation destination
- Handles lifecycle automatically
Manual Injection (Advanced)
For non-ViewModel dependencies in Composables:Testing with Hilt
Hilt makes testing easier by allowing you to replace dependencies.ViewModel Test (Without Hilt)
Repository Test (Without Hilt)
Integration Tests with Hilt
For tests that need the full dependency graph:Common Patterns
Providing Multiple Implementations
Use qualifiers when you need multiple instances of the same type:Optional Dependencies
Best Practices
Prefer Constructor Injection
Prefer Constructor Injection
Use
@Inject constructor over field injection. Constructor injection:- Makes dependencies explicit
- Enables manual instantiation in tests
- Ensures all dependencies are provided
Use @Binds When Possible
Use @Binds When Possible
@Binds is more efficient than @Provides for simple interface bindings.Scope Appropriately
Scope Appropriately
Use the narrowest scope possible:
@Singleton: Only for truly app-wide dependencies@ViewModelScoped: For ViewModel-specific dependencies- No scope: New instance every time (stateless utilities)
Organize Modules by Functionality
Organize Modules by Functionality
Separate modules by concern:
NetworkModule: Network dependenciesRepositoryModule: Repository bindingsDatabaseModule: Database dependencies (if added)
Depend on Abstractions
Depend on Abstractions
Inject interfaces, not implementations.
Troubleshooting
Missing @HiltAndroidApp
Missing @HiltAndroidApp
Error:
Hilt components are not generatedSolution: Add @HiltAndroidApp to your Application class.Missing @AndroidEntryPoint
Missing @AndroidEntryPoint
Error:
ViewModel not found or ViewModelProvider.Factory not setSolution: Add @AndroidEntryPoint to your Activity/Fragment.Circular Dependencies
Circular Dependencies
Error:
Dependency cycle detectedSolution:- Restructure dependencies to break the cycle
- Use
Provider<T>orLazy<T>to delay initialization
Unbound Interface
Unbound Interface
Error:
MyInterface cannot be provided without an @Provides or @Binds annotated methodSolution: Add a binding in a module:Summary
Hilt in TecMeli
- Setup:
@HiltAndroidAppon Application,@AndroidEntryPointon Activity - Modules:
NetworkModule(network deps),RepositoryModule(repository bindings) - Injection:
@Inject constructorfor classes,@Providesfor configuration - Scopes:
@Singletonfor app-wide,@HiltViewModelfor ViewModels - Testing: Constructor injection enables easy mocking
- Benefits: Automatic dependency resolution, lifecycle management, testability
Related Topics
Clean Architecture
See how DI supports Clean Architecture principles
MVVM Pattern
Learn how ViewModels receive dependencies via Hilt