Dagger / Hilt: wat is het, DI en toepassing

Auteur: IT Sectr Gepubliceerd: 2026-05-03 Leestijd: 9 min

Dagger — is een dependency-injectieframework voor Java en Kotlin dat DI-code genereert tijdens compilatie door annotatieverwerking. Hilt — is een laag bovenop Dagger voor Android die de configuratie van componenten en levenscyclus vereenvoudigt. Volgens Google, 2025 wordt Hilt gebruikt in meer dan 70% van de Android-apps uit de Google Play Top-100, met ondersteuning voor Activity, Fragment, ViewModel en Service via vooraf gedefinieerde componenten. Beide frameworks zorgen voor compile-time verificatie van de dependency-graaf, waardoor runtime-injectiefouten worden geëlimineerd.

Belangrijkste punten

  • Dagger — compile-time DI-framework met codegeneratie via @Module, @Provides, @Component annotaties
  • Hilt — Android-laag die Dagger vereenvoudigt via @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel
  • Component — dependency-graaf die Module met Inject-doelen verbindt via proxymethoden
  • Scope — @Singleton, @ViewModelScoped, @ActivityScoped beheren de levensduur van geïnjecteerde objecten
  • Hilt ondersteunt multimodule-projecten via @InstallIn voor geïsoleerde dependency-grafen

Wat is Dagger / Hilt?

Dagger — is een dependency-injectieframework met codegeneratie tijdens compilatie (compile-time DI). Ontwikkeld bij Square en later overgedragen aan Google, gebruikt Dagger de Java APT-annotatieprocessor om de dependency-graaf te analyseren en fabrieksklassen te genereren. In tegenstelling tot runtime-DI (Guice, Koin) gebruikt Dagger geen reflectie — alle code wordt tijdens compilatie gemaakt, wat maximale prestaties tijdens runtime en foutdetectie tijdens de build garandeert.

Hilt — is een bibliotheek van Google, gebouwd bovenop Dagger en geoptimaliseerd voor Android. Hilt biedt vooraf gedefinieerde componenten die overeenkomen met de levenscyclus van Android-componenten: @SingletonComponent voor Application, @ActivityComponent voor Activity, @FragmentComponent voor Fragment, @ViewModelComponent voor ViewModel. Dit elimineert de routinematige configuratie van Component en Module die in pure Dagger nodig is. Hilt genereert ook automatisch de dependency-graaf voor elke Android-component via @AndroidEntryPoint.

Volgens Google I/O 2024 is Hilt de aanbevolen DI-oplossing voor Android-apps geschreven in Kotlin. Jetpack-bibliotheken (Navigation, Room, WorkManager) hebben ingebouwde integratie met Hilt via @HiltViewModel en @HiltWorker. In projecten die geen Android gebruiken (pure Java/Kotlin-bibliotheken, servertoepassingen) wordt pure Dagger zonder Hilt-laag toegepast.

Probleem van handmatige dependency injection

Zonder DI-framework maakt de ontwikkelaar objecten handmatig via constructors of fabrieken, waarbij afhankelijkheden door de keten worden doorgegeven. Elke nieuwe vereiste — wijziging van de handtekening van alle constructors in de keten. Dagger automatiseert dit proces: het is voldoende om te declareren welk type nodig is (@Inject constructor), en Dagger zal de dependency-graaf maken, waarbij alle geneste typen worden opgelost. Bij wijziging van afhankelijkheden werkt Dagger de gegenereerde code automatisch bij — fouten in de keten zijn onmogelijk.

Principes van dependency injection

Dependency injection — is een patroon waarbij een object zijn afhankelijkheden van buitenaf ontvangt, in plaats van ze zelf te maken. DI implementeert het principe van inversie van controle (IoC): de klasse is niet verantwoordelijk voor het maken van zijn eigen afhankelijkheden, maar declareert ze via constructor, methode of veld. Injectie via constructor (constructor injection) wordt als de meest geprefereerde methode beschouwd, omdat het garandeert dat het object in een geldige staat wordt gemaakt.

InjectietypeDagger-syntaxWanneer gebruiken
Constructor injection@Inject constructorHoofdmethode — voor alle eigen klassen
Field injection@Inject lateinit varAlleen voor Android-componenten (Activity, Fragment)
Method injection@Inject fun bind()Voor post-construct initialisatie

Voordelen van compile-time DI

De belangrijkste voordelen van DI zijn testbaarheid (afhankelijkheden kunnen worden vervangen door mock-objecten), losse koppeling (klassen zijn afhankelijk van interfaces, niet van implementaties) en expliciet beheer van de levenscyclus van objecten via scopes. Dagger garandeert automatisch dat een object eenmaal wordt gemaakt binnen zijn scope en wordt vernietigd bij het verlaten van de scope.

Architectuur van Dagger: Component, Module, Provides

Component — het centrale element van de Dagger dependency-graaf. Het is een interface geannoteerd met @Component die de brug beschrijft tussen Module en injectiedoelen. Dagger genereert de implementatie van Component (bijv. DaggerAppComponent) tijdens compilatie. Component bepaalt welke typen beschikbaar zijn voor injectie, via abstracte methoden die de vereiste typen retourneren of via inject-methoden die een object accepteren voor field injection.

kotlin
// Module: levert afhankelijkheden die Dagger niet zelf kan maken
@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideApiService(client: OkHttpClient): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Component: verbindt Module en injectiedoelen
@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
    fun getApiService(): ApiService
}

@Module — is een klasse die methoden met @Provides bevat die instanties van afhankelijkheden retourneren. Module wordt gebruikt voor typen die Dagger niet automatisch kan maken: bibliotheken van derden (OkHttp, Retrofit), objecten met constructorparameters, interfaces met implementatiekeuze. @Binds — een alternatief voor @Provides in gevallen waar de methode een interface retourneert en een enkele implementatie accepteert: Dagger genereert een directe cast zonder de methode aan te roepen.

@Scope bepaalt de levensduur van een object in de dependency-graaf. @Singleton — het object wordt eenmaal gemaakt voor de hele app. @ActivityScoped — het object leeft zolang de Activity leeft. @FragmentScoped — zolang het Fragment leeft. Zonder scope maakt Dagger een nieuwe instantie bij elke injectie. @Reusable — scope voor objecten die geen singleton hoeven te zijn, maar waarvan het maken duur is — Dagger kan de instantie cachen, maar garandeert dit niet.

Hilt voor Android: @HiltAndroidApp en @AndroidEntryPoint

Hilt vereenvoudigt de configuratie van Dagger voor Android dankzij vooraf gedefinieerde componenten en automatische generatie van de basisgraaf. De annotatie @HiltAndroidApp op de Application-klasse activeert de generatie van de Hilt-component. Zonder deze annotatie werkt Hilt niet — deze is verplicht voor elke Android-app die Hilt gebruikt. @HiltAndroidApp maakt de oudercomponent SingletonComponent aan, waarvan alle andere componenten van de app overerven.

kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // apiService is al geïnjecteerd vóór de aanroep van onCreate
    }
}

@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext ctx: Context): AppDatabase {
        return Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
    }
}

@AndroidEntryPoint — annotatie voor Activity, Fragment, Service, BroadcastReceiver en View. Genereert een Hilt-component voor elk type: @AndroidEntryPoint op Activity maakt ActivityComponent aan, dat overerft van SingletonComponent. De kindcomponent ontvangt automatisch alle afhankelijkheden van de ouder. Field injection met @Inject lateinit var is alleen beschikbaar in klassen geannoteerd met @AndroidEntryPoint — in gewone klassen wordt constructor injection gebruikt.

@InstallIn geeft aan in welke Hilt-component de module wordt geïnstalleerd. NetworkModule met @InstallIn(SingletonComponent::class) is beschikbaar in de hele app. Module met @InstallIn(ActivityComponent::class) — alleen in Activity. Dit isoleert dependency-grafen: Activity-specifieke modules zijn niet zichtbaar in Fragment en ViewModel, waardoor onbedoeld gebruik van niet-toegestane afhankelijkheden wordt voorkomen. @ApplicationContext — de ingebouwde Hilt-kwalificatie voor het verkrijgen van de applicatiecontext.

Qualifier: @Named en aangepaste kwalificaties

Wanneer twee verschillende implementaties van dezelfde interface moeten worden geïnjecteerd, worden kwalificaties gebruikt. Hilt ondersteunt @Named voor tekstuele identificatie en aangepaste annotaties met @Qualifier. Bijvoorbeeld @Named("baseUrl") en @Named("imageBaseUrl") voor verschillende tekstuele configuraties. Aangepaste kwalificaties hebben de voorkeur boven @Named vanwege controle tijdens compilatie — een onjuiste tekstnaam wordt pas tijdens runtime gedetecteerd.

Hilt ViewModel: @HiltViewModel en @Inject constructor

@HiltViewModel — annotatie die de handmatige ViewModelProvider.Factory-fabriek vervangt. Een klasse geannoteerd met @HiltViewModel met @Inject constructor ontvangt automatisch alle afhankelijkheden via Dagger. Hilt genereert ViewModelFactory die wordt gebruikt door Jetpack ViewModelProvider. Zonder Hilt zou de ontwikkelaar de fabriek handmatig moeten schrijven en elke parameter uit Activity of fragment moeten doorgeven.

kotlin
@HiltViewModel
class MainViewModel
    @Inject constructor(
        private val apiService: ApiService,
        private val database: AppDatabase
    ) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    fun loadUsers() {
        viewModelScope.launch {
            _users.value = apiService.getUsers()
        }
    }
}

// In Activity — Hilt maakt automatisch ViewModel aan
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

ViewModelScoped — Hilt-scope voor afhankelijkheden die leven zolang de ViewModel leeft. Als twee ViewModels van hetzelfde type dezelfde @ViewModelScoped-afhankelijkheid injecteren, krijgt elk zijn eigen instantie. Dit onderscheidt @ViewModelScoped van @ActivityScoped, waar één Activity één instantie krijgt voor alle fragments. Voor ViewModel-specifieke afhankelijkheden (bijv. SavedStateHandle) wordt @HiltViewModel met @Inject constructor(savedStateHandle: SavedStateHandle) gebruikt.

Hilt ondersteunt assisted injection via de Hilt Extensions-bibliotheek. Assisted injection maakt het mogelijk parameters aan de constructor door te geven tijdens injectie, wanneer een deel van de afhankelijkheden alleen tijdens runtime bekend is (bijv. gebruikers-ID uit intent). Voor assisted injection wordt @AssistedInject gebruikt in combinatie met @Assisted-parameters. Hilt genereert AssistedFactory, die op standaardwijze kan worden geïnjecteerd.

Dagger vs Hilt: vergelijking en migratie

Pure Dagger vereist handmatige creatie van Component, definiëren van scopes en configuratie van injectie in elke Android-component. De ontwikkelaar maakt AppComponent, ActivityComponent, FragmentComponent en beheert hun relaties via @Subcomponent. Deze aanpak biedt maximale controle, maar vereist aanzienlijke hoeveelheid boilerplate-code. Dagger wordt gebruikt in grote projecten waar een aangepaste DI-architectuur nodig is, of in niet-Android Java/Kotlin-projecten.

Hilt automatiseert boilerplate: één @HiltAndroidApp, één @AndroidEntryPoint voor elke component, vooraf gedefinieerde scopes. Hilt wordt door Google aanbevolen voor alle nieuwe Android-projecten. Migratie van Dagger naar Hilt omvat het vervangen van Component door @InstallIn, het vervangen van @Subcomponent door vooraf gedefinieerde Hilt-componenten en het vervangen van handmatige ViewModelProvider.Factory door @HiltViewModel. De meeste @Module-klassen worden overgezet met toevoeging van @InstallIn zonder wijziging van @Provides-methoden.

KenmerkDaggerHilt
ConfiguratieHandmatig: Component, Subcomponent, BuilderAutomatisch: @HiltAndroidApp, @AndroidEntryPoint
Android-componentenGeen vooraf gedefinieerde12+ ingebouwde componenten
ViewModelHandmatige fabriek@HiltViewModel + @Inject constructor
MultimoduleVia @Component(dependencies)Via @InstallIn + aggregatie
ComplexiteitHoog — ervaring vereistLaag — intuïtief
FlexibiliteitMaximaalStandaard (dekt 95% van scenario's)

Beperkingen van Hilt: de bibliotheek ondersteunt alleen Android (niet geschikt voor puur server-Java-projecten), legt een bepaalde componentstructuur op (moeilijk te overschrijven) en voegt een afhankelijkheid van android.hilt:hilt-navigation-compose toe voor Jetpack Compose-projecten. Voor Compose-apps biedt Hilt @HiltViewModel beschikbaar in Composable via hiltViewModel() — zonder handmatige levering van ViewModel uit Activity.

Veelgestelde vragen

Wat is het verschil tussen Dagger en Hilt?

Dagger — basis compile-time DI-framework met handmatige configuratie van Component en Module. Hilt — Android-laag die het maken van componenten en integratie met de levenscyclus van Activity, Fragment, ViewModel, Service en BroadcastReceiver automatiseert.

Waar dient @HiltAndroidApp voor?

@HiltAndroidApp activeert de generatie van de Hilt-component voor Application. Zonder deze annotatie kan Hilt de basiscomponent SingletonComponent niet maken, waarvan alle ActivityComponent, FragmentComponent en ViewModelComponent overerven. De annotatie is verplicht voor elk Hilt-project.

Hoe werkt Hilt met Jetpack Navigation?

Hilt Navigation biedt @HiltViewModel voor ViewModel in NavBackStackEntry en hiltNavGraphViewModels() voor het scopën van ViewModel binnen de navigatiegraaf. De bibliotheek android.hilt:hilt-navigation-fragment maakt automatisch ViewModel aan voor elke NavBackStackEntry.

Hoe injecteer ik Context in Hilt?

Gebruik @ApplicationContext voor de applicatiecontext of @ActivityContext voor de Activity-context. Hilt biedt deze kwalificaties ingebouwd in de bibliotheek android.hilt:hilt-android. @ActivityContext is alleen beschikbaar in modules geïnstalleerd in ActivityComponent.

Wat is @Binds en wanneer wordt het gebruikt?

@Binds — een efficiënt alternatief voor @Provides, wanneer de methode precies één parameter accepteert en het type als interface retourneert. @Binds genereert een directe cast zonder de methode aan te roepen, wat de hoeveelheid gegenereerde code vermindert en de prestaties tijdens injectie verbetert.

Samenvatting

  • Dagger — compile-time DI-framework met @Module, @Provides, @Component annotaties en codegeneratie via APT
  • Hilt — Android-laag bovenop Dagger met @HiltAndroidApp, @AndroidEntryPoint, @InstallIn en vooraf gedefinieerde componenten
  • Component beheert de dependency-graaf, Module levert klassen van derden, Provides — objectfabrieken
  • Scope (@Singleton, @ViewModelScoped, @ActivityScoped) bepaalt de levensduur van het object in de Dagger-graaf
  • @HiltViewModel automatiseert het maken van ViewModel, waardoor handmatige ViewModelProvider.Factory-fabrieken worden geëlimineerd
  • @InstallIn isoleert modules per component, waardoor lekkage van afhankelijkheden tussen lagen van de app wordt voorkomen
  • Hilt wordt door Google aanbevolen voor alle nieuwe Android-projecten, Dagger — voor niet-Android en aangepaste DI-architecturen

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook