Android Dagger 2 Dependency Injection¶
Dagger 2 generates dependency injection code at compile time with zero reflection and zero runtime overhead. It connects providers (Modules) to consumers (@Inject) through a Component interface.
Key Facts¶
- Dagger 2 generates DI code at compile time - no reflection, no runtime overhead
@Moduleclasses contain@Providesmethods that create dependency instances@Componentinterface connects@Injectrequests to@Moduleproviders@Singletonscope creates one instance and reuses it everywhere@Injectrequests injection of a dependency (constructor or field)@Named("qualifier")disambiguates when multiple same-type dependencies exist- Build the project to generate
DaggerAppComponentclass
Patterns¶
Core Annotations¶
| Annotation | Purpose |
|---|---|
@Module | Class that provides dependencies |
@Provides | Method inside Module that creates an instance |
@Singleton | Create once, reuse everywhere |
@Component | Interface connecting inject requests to providers |
@Inject | Request injection of a dependency |
@Named("x") | Qualifier when multiple same-type deps exist |
@Binds | Abstract method to bind implementation to interface |
@IntoMap | Contribute to a Map of dependencies |
Module Examples¶
@Module
class AppModule(private val app: App) {
@Provides
@Singleton
fun provideContext(): Context = app
}
@Module
class RestModule {
@Provides
@Singleton
fun provideGson(): Gson = GsonBuilder().setLenient().create()
@Provides
@Singleton
@Named("COINGECKO_API")
fun provideRetrofit(gson: Gson, client: OkHttpClient): Retrofit =
Retrofit.Builder()
.baseUrl("https://api.coingecko.com/api/v3/")
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build()
}
Component Interface¶
@Singleton
@Component(modules = [
AppModule::class,
RestModule::class,
ViewModelModule::class
])
interface AppComponent {
fun inject(activity: MainActivity)
fun inject(fragment: CurrenciesListFragment)
}
Application Class Setup¶
class App : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
}
Register in AndroidManifest.xml:
ViewModel Injection¶
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(AccountViewModel::class)
abstract fun bindAccountViewModel(
viewModel: AccountViewModel
): ViewModel
}
// In Fragment
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(requireActivity().application as App).appComponent.inject(this)
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(AccountViewModel::class.java)
}
Injection in Fragment¶
class CurrenciesListFragment : Fragment() {
@Inject lateinit var apiService: CoinGeckoApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(requireActivity().application as App).appComponent.inject(this)
}
}
Gotchas¶
DaggerAppComponentis generated at compile time - build the project after defining the Component- Every Fragment/Activity that uses
@Injectmust have a correspondinginject()method in the Component @Singletonscope is tied to the Component's lifetime - not truly global if Component is recreated@Namedqualifier is a string - typos cause runtime injection failures- Dagger 2 error messages can be cryptic - check that all dependencies have providers
- Modern alternative: Hilt (built on Dagger) simplifies setup with
@HiltAndroidApp,@AndroidEntryPoint
See Also¶
- [[android-mvvm-architecture]] - ViewModels consuming injected dependencies
- [[android-retrofit-networking]] - providing Retrofit via Dagger modules
- [[android-room-database]] - providing Room database via Dagger
- [[kotlin-android-fundamentals]] - companion object, lateinit