Dependency injection.
Classes depend on other classes. A ViewModel needs a Repository. A Repository needs an API and a Database. How do you provide these dependencies? Dependency injection is the answer. Today we understand DI and how to use Hilt in Android.
Classes create their dependencies internally.
class UserViewModel brace val repository equals UserRepository close brace.
The ViewModel creates its own Repository. Tight coupling. Hard to change.
What if you want a different Repository? What if you want to test with a fake? You cannot. The ViewModel controls the dependency.
This makes code rigid. Changes ripple through. Testing requires real implementations.
With DI, dependencies are provided from outside.
class UserViewModel repository colon UserRepository brace use repository close brace.
The constructor receives the Repository. The ViewModel does not create it. Someone else provides it.
This inverts control. The class says what it needs. Something else provides it.
Now you can provide different implementations. Real for production. Fake for testing. The class does not change.
Testability.
Pass fake dependencies in tests. No network, no database needed. Fast, isolated tests.
Modularity.
Swap implementations without changing dependent code. Different database for debug builds.
Single responsibility.
Classes focus on their job. Not on creating dependencies.
Configuration centralized.
One place decides what implementations to use. Not scattered across classes.
You can do DI manually.
Pass dependencies through constructors. Create them at the top level. Wire everything together.
This works for small apps. It becomes tedious for large apps. Many dependencies. Many layers.
DI frameworks automate the wiring. Hilt for Android. Dagger under the hood.
You declare what you need. The framework provides it. Automatic. Type-safe.
Hilt is the recommended DI framework for Android.
Add Hilt plugin and dependencies. Annotate your Application class.
at HiltAndroidApp class MyApplication colon Application.
Annotate Activities and Fragments. at AndroidEntryPoint class MainActivity.
Inject dependencies. at Inject lateinit var repository colon UserRepository.
Hilt provides the implementation automatically.
Tell Hilt how to create objects.
Constructor injection is simplest. at Inject constructor on the class.
class UserRepository at Inject constructor private val api colon UserApi, private val dao colon UserDao.
Hilt sees at Inject and knows how to create it.
For interfaces or classes you do not own, use modules.
at Module at InstallIn SingletonComponent class AppModule at Provides fun provideRetrofit colon Retrofit.
at Provides tells Hilt how to create the object.
Scopes control object lifetime.
at Singleton lives for the app lifetime. One instance shared everywhere.
at ViewModelScoped lives for the ViewModel lifetime. Per-ViewModel instance.
at ActivityScoped lives for the Activity lifetime. New instance per Activity.
Use at Singleton for shared resources. Retrofit, Database.
Use at ViewModelScoped for per-screen data.
No scope annotation means new instance each time. Be intentional about scopes.
ViewModels need special handling.
at HiltViewModel class UserViewModel at Inject constructor repository colon UserRepository colon ViewModel.
at HiltViewModel marks it for Hilt injection.
at Inject constructor receives dependencies. Hilt provides them.
In Composable, use hiltViewModel. at Composable fun UserScreen viewModel colon UserViewModel equals hiltViewModel.
Hilt creates the ViewModel with all its dependencies.
Let us summarize.
Dependency injection provides dependencies from outside.
Benefits: testability, modularity, single responsibility.
Hilt automates dependency wiring in Android.
Use at Inject for constructor injection. Use at Provides for modules.
Scopes control lifetime: Singleton, ViewModelScoped, ActivityScoped.
at HiltViewModel enables ViewModel injection.
DI makes large apps manageable and testable.