Dependency Injection: Hilt, Dagger, Koin
Dependency Injection means an object does not create its own dependencies — they are supplied from outside. The ViewModel does not new a Repository — it receives one in its constructor.
Why it matters:
- Testing — a fake repository can replace the real network
- A single wiring point — objects like Retrofit/Room are configured in one place
- Loose coupling — depend on interfaces, swap implementations
The tools:
- Dagger 2 — compile-time DI: code generation, fast at runtime, steep learning curve.
- Hilt — Dagger's standardized Android face: predefined components (
SingletonComponent,ViewModelComponent),@HiltViewModel,@AndroidEntryPoint. Google's recommendation. - Koin — service-locator style, runtime resolution: a simple DSL, no codegen, but errors surface at runtime and large graphs resolve slower.
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides @Singleton
fun provideApi(retrofit: Retrofit): AccountApi =
retrofit.create(AccountApi::class.java)
}
class AccountRepositoryImpl @Inject constructor(
private val api: AccountApi,
private val dao: AccountDao
) : AccountRepository { ... }
@HiltViewModel
class AccountViewModel @Inject constructor(
private val repo: AccountRepository
) : ViewModel() { ... }
@AndroidEntryPoint
class AccountsFragment : Fragment() {
private val viewModel: AccountViewModel by viewModels()
}A typical Hilt graph: module → repository → ViewModel
Scope questions always come: @Singleton — one instance app-wide (Retrofit, Room); @ViewModelScoped — for the ViewModel's lifetime; an unscoped binding yields a new object per injection. Wrong scoping is a real bug: make the user session @Singleton carelessly and stale data can survive logout — in a banking app that is a serious security defect.
🛠 Practice task
Build a small Hilt graph: @Module → a Repository interface + implementation → @HiltViewModel.
- Delete one binding on purpose and read the compile error — feel Dagger/Hilt's compile-time advantage.
- Log the difference between
@Singletonand an unscoped binding (print object hashes). - Write a
FakeRepositoryand get one unit test passing against the ViewModel.
Done when: you can construct the ViewModel in a test without starting any Hilt component.
📚 Sources and documentation
- Dependency injection with Hiltofficialdeveloper.android.com
- DI overview on Androidofficialdeveloper.android.com
- Koin documentationofficialinsert-koin.io