Dagger ist ein Dependency-Injection-Framework für Java und Kotlin, das DI-Code zur Kompilierzeit durch Annotation Processing generiert. Hilt ist ein Wrapper um Dagger für Android, der die Komponenteneinrichtung und das Lebenszyklusmanagement vereinfacht. Laut Google, 2025 wird Hilt in mehr als 70 % der Android-Apps aus den Google Play Top-100 verwendet und unterstützt Activity, Fragment, ViewModel und Service durch vordefinierte Komponenten. Beide Frameworks bieten eine Kompilierzeit-Überprüfung des Abhängigkeitsgraphen und eliminieren Laufzeit-Injektionsfehler.
Wichtige Punkte
Dagger ist ein Dependency-Injection-Framework mit Compilezeit-Codegenerierung. Ursprünglich bei Square entwickelt und später an Google übergeben, verwendet Dagger den Java-APT-Annotationsprozessor, um den Abhängigkeitsgraphen zu analysieren und Factory-Klassen zu generieren. Im Gegensatz zu Laufzeit-DI (Guice, Koin) verwendet Dagger keine Reflection — der gesamte Code wird zur Kompilierzeit erstellt, was maximale Laufzeitleistung und Fehlererkennung zur Buildzeit gewährleistet.
Hilt ist eine Google-Bibliothek, die auf Dagger aufbaut und für Android optimiert ist. Hilt bietet vordefinierte Komponenten, die dem Lebenszyklus von Android-Komponenten entsprechen: @SingletonComponent für Application, @ActivityComponent für Activity, @FragmentComponent für Fragment, @ViewModelComponent für ViewModel. Dies eliminiert die routinemäßige Component- und Module-Konfiguration, die in reinem Dagger erforderlich ist. Hilt generiert außerdem automatisch den Abhängigkeitsgraphen für jede Android-Komponente über @AndroidEntryPoint.
Laut Google I/O 2024 ist Hilt die empfohlene Lösung für DI in Android-Anwendungen, die in Kotlin geschrieben sind. Jetpack-Bibliotheken (Navigation, Room, WorkManager) haben eine integrierte Hilt-Integration über @HiltViewModel und @HiltWorker. In Projekten, die kein Android verwenden (reine Java/Kotlin-Bibliotheken, Serveranwendungen), wird reines Dagger ohne den Hilt-Wrapper verwendet.
Ohne ein DI-Framework erstellt der Entwickler Objekte manuell über Konstruktoren oder Fabriken und übergibt Abhängigkeiten entlang der Kette. Jede neue Anforderung bedeutet, die Signaturen aller Konstruktoren in der Kette zu ändern. Dagger automatisiert diesen Prozess: Sie müssen nur deklarieren, welcher Typ benötigt wird (@Inject constructor), und Dagger erstellt den Abhängigkeitsgraphen und löst alle verschachtelten Typen auf. Wenn sich Abhängigkeiten ändern, aktualisiert Dagger den generierten Code automatisch — Fehler in der Kette sind unmöglich.
Dependency Injection ist ein Muster, bei dem ein Objekt seine Abhängigkeiten von außen erhält, anstatt sie selbst zu erstellen. DI implementiert das Prinzip der Inversion of Control (IoC): Eine Klasse ist nicht für die Erstellung ihrer eigenen Abhängigkeiten verantwortlich, sondern deklariert sie über einen Konstruktor, eine Methode oder ein Feld. Die Konstruktorinjektion wird als die bevorzugteste Methode angesehen, da sie garantiert, dass das Objekt in einem gültigen Zustand erstellt wird.
| Injektionstyp | Dagger-Syntax | Wann verwenden |
|---|---|---|
| Constructor injection | @Inject constructor | Primäre Methode — für alle eigenen Klassen |
| Field injection | @Inject lateinit var | Nur für Android-Komponenten (Activity, Fragment) |
| Method injection | @Inject fun bind() | Für Post-Konstrukt-Initialisierung |
Die Hauptvorteile von DI umfassen Testbarkeit (Abhängigkeiten können durch Mock-Objekte ersetzt werden), lose Kopplung (Klassen hängen von Schnittstellen ab, nicht von Implementierungen) und explizites Lebenszyklusmanagement von Objekten durch Scopes. Dagger garantiert automatisch, dass ein Objekt einmal innerhalb seines Scopes erstellt und beim Verlassen des Scopes zerstört wird.
Component ist das zentrale Element des Dagger-Abhängigkeitsgraphen. Es ist ein mit @Component annotiertes Interface, das die Brücke zwischen Module und Injektionszielen beschreibt. Dagger generiert die Component-Implementierung (z. B. DaggerAppComponent) zur Kompilierzeit. Die Component bestimmt, welche Typen für die Injektion verfügbar sind, über abstrakte Methoden, die die erforderlichen Typen zurückgeben, oder über inject-Methoden, die ein Objekt für die Feldinjektion akzeptieren.
// Module: stellt Abhängigkeiten bereit, die Dagger nicht selbst erstellen kann
@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: verbindet Module und Injection-Ziele
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module ist eine Klasse, die Methoden mit @Provides enthält, die Instanzen von Abhängigkeiten zurückgeben. Module wird für Typen verwendet, die Dagger nicht automatisch erstellen kann: Drittanbieter-Bibliotheken (OkHttp, Retrofit), Objekte mit Konstruktorparametern, Schnittstellen mit Implementierungsauswahl. @Binds ist eine Alternative zu @Provides für Fälle, in denen eine Methode eine Schnittstelle zurückgibt und eine einzige Implementierung akzeptiert: Dagger generiert einen direkten Cast ohne Methodenaufruf.
@Scope definiert die Lebensdauer eines Objekts im Abhängigkeitsgraphen. @Singleton — das Objekt wird einmal für die gesamte Anwendung erstellt. @ActivityScoped — das Objekt lebt, solange die Activity lebt. @FragmentScoped — solange das Fragment lebt. Ohne Scope erstellt Dagger bei jeder Injektion eine neue Instanz. @Reusable — ein Scope für Objekte, die keine Singletons sein müssen, deren Erstellung jedoch teuer ist — Dagger kann die Instanz cachen, garantiert dies jedoch nicht.
Hilt vereinfacht die Dagger-Konfiguration für Android durch vordefinierte Komponenten und automatische Basisgraphengenerierung. Die @HiltAndroidApp-Annotation auf der Application-Klasse löst die Hilt-Komponentengenerierung aus. Ohne diese Annotation funktioniert Hilt nicht — sie ist für jede Android-Anwendung, die Hilt verwendet, obligatorisch. @HiltAndroidApp erstellt die übergeordnete SingletonComponent, von der alle anderen Anwendungskomponenten erben.
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// apiService bereits vor dem onCreate-Aufruf injiziert
}
}
@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 ist eine Annotation für Activity, Fragment, Service, BroadcastReceiver und View. Sie generiert eine Hilt-Komponente für jeden Typ: @AndroidEntryPoint auf einer Activity erstellt eine ActivityComponent, die von SingletonComponent erbt. Die untergeordnete Komponente erhält automatisch alle Abhängigkeiten der übergeordneten. Field Injection mit @Inject lateinit var ist nur in Klassen verfügbar, die mit @AndroidEntryPoint annotiert sind — in regulären Klassen wird Constructor Injection verwendet.
@InstallIn gibt an, in welche Hilt-Komponente ein Modul installiert wird. NetworkModule mit @InstallIn(SingletonComponent::class) ist in der gesamten Anwendung verfügbar. Ein Module mit @InstallIn(ActivityComponent::class) ist nur in Activity verfügbar. Dies isoliert Abhängigkeitsgraphen: Activity-spezifische Module sind in Fragment und ViewModel nicht sichtbar und verhindern so die versehentliche Verwendung ungültiger Abhängigkeiten. @ApplicationContext ist ein eingebauter Hilt-Qualifizierer zum Abrufen des Anwendungs-Context.
Wenn zwei verschiedene Implementierungen derselben Schnittstelle injiziert werden müssen, werden Qualifizierer verwendet. Hilt unterstützt @Named für String-Identifikatoren und benutzerdefinierte Annotationen mit @Qualifier. Zum Beispiel @Named("baseUrl") und @Named("imageBaseUrl") für verschiedene String-Konfigurationen. Benutzerdefinierte Qualifizierer sind aufgrund der Kompilierzeitprüfung @Named vorzuziehen — ein falscher String-Name wird erst zur Laufzeit erkannt.
@HiltViewModel ist eine Annotation, die die manuelle ViewModelProvider.Factory ersetzt. Eine mit @HiltViewModel annotierte Klasse mit @Inject constructor erhält automatisch alle Abhängigkeiten über Dagger. Hilt generiert eine ViewModelFactory, die von Jetpack ViewModelProvider verwendet wird. Ohne Hilt muss der Entwickler die Factory manuell schreiben und dabei jeden Parameter aus der Activity oder dem Fragment übergeben.
@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 erstellt automatisch ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
ViewModelScoped ist ein Hilt-Scope für Abhängigkeiten, die leben, solange das ViewModel lebt. Wenn zwei ViewModels desselben Typs dieselbe @ViewModelScoped-Abhängigkeit injizieren, erhält jede ihre eigene Instanz. Dies unterscheidet @ViewModelScoped von @ActivityScoped, wo eine Activity eine Instanz für alle Fragmente erhält. Für ViewModel-spezifische Abhängigkeiten (z. B. SavedStateHandle) wird @HiltViewModel mit @Inject constructor(savedStateHandle: SavedStateHandle) verwendet.
Hilt unterstützt assisted injection über die Hilt Extensions-Bibliothek. Assisted Injection ermöglicht die Übergabe von Parametern an den Konstruktor zum Zeitpunkt der Injektion, wenn einige Abhängigkeiten erst zur Laufzeit bekannt sind (z. B. Benutzer-ID aus einem Intent). Für Assisted Injection wird @AssistedInject in Kombination mit @Assisted-Parametern verwendet. Hilt generiert eine AssistedFactory, die auf standardmäßige Weise injiziert werden kann.
Reines Dagger erfordert die manuelle Erstellung von Component, die Definition von Scopes und die Konfiguration der Injektion in jede Android-Komponente. Der Entwickler erstellt AppComponent, ActivityComponent, FragmentComponent und verwaltet ihre Beziehungen über @Subcomponent. Dieser Ansatz bietet maximale Kontrolle, erfordert jedoch erheblichen Boilerplate-Code. Dagger wird in großen Projekten verwendet, die eine nicht standardmäßige DI-Architektur erfordern, oder in Nicht-Android-Java/Kotlin-Projekten.
Hilt automatisiert den Boilerplate: ein @HiltAndroidApp, ein @AndroidEntryPoint für jede Komponente, vordefinierte Scopes. Google empfiehlt Hilt für alle neuen Android-Projekte. Die Migration von Dagger zu Hilt umfasst das Ersetzen von Component durch @InstallIn, das Ersetzen von @Subcomponent durch vordefinierte Hilt-Komponenten und das Ersetzen der manuellen ViewModelProvider.Factory durch @HiltViewModel. Die meisten @Module-Klassen werden durch Hinzufügen von @InstallIn migriert, ohne die @Provides-Methoden zu ändern.
| Merkmal | Dagger | Hilt |
|---|---|---|
| Einrichtung | Manuell: Component, Subcomponent, Builder | Automatisch: @HiltAndroidApp, @AndroidEntryPoint |
| Android-Komponenten | Keine vordefinierten | 12+ integrierte Komponenten |
| ViewModel | Manuelle Factory | @HiltViewModel + @Inject constructor |
| Multimodul | Über @Component(dependencies) | Über @InstallIn + Aggregation |
| Komplexität | Hoch — Erfahrung erforderlich | Niedrig — intuitiv verständlich |
| Flexibilität | Maximal | Standard (deckt 95 % der Szenarien ab) |
Einschränkungen von Hilt: Die Bibliothek unterstützt nur Android (nicht geeignet für reine serverseitige Java-Projekte), erzwingt eine bestimmte Komponentenstruktur (schwer zu überschreiben) und fügt eine Abhängigkeit zu android.hilt:hilt-navigation-compose für Jetpack Compose-Projekte hinzu. Für Compose-Anwendungen bietet Hilt @HiltViewModel, das in Composable über hiltViewModel() zugänglich ist — ohne manuelle ViewModel-Bereitstellung aus der Activity.
Häufig gestellte Fragen
Dagger ist ein grundlegendes Compilezeit-DI-Framework mit manueller Component- und Module-Konfiguration. Hilt ist ein Android-Wrapper, der die Komponentenerstellung und Integration mit dem Lebenszyklus von Activity, Fragment, ViewModel, Service und BroadcastReceiver automatisiert.
@HiltAndroidApp aktiviert die Hilt-Komponentengenerierung für Application. Ohne diese Annotation kann Hilt die basis SingletonComponent nicht erstellen, von der alle ActivityComponent, FragmentComponent und ViewModelComponent erben. Die Annotation ist für jedes Hilt-Projekt obligatorisch.
Hilt Navigation bietet @HiltViewModel für ViewModel in NavBackStackEntry und hiltNavGraphViewModels() zum Scopen von ViewModel innerhalb des Navigationsgraphen. Die Bibliothek android.hilt:hilt-navigation-fragment erstellt automatisch ein ViewModel für jeden NavBackStackEntry.
Verwenden Sie @ApplicationContext für den Anwendungskontext oder @ActivityContext für den Activity-Kontext. Hilt stellt diese Qualifizierer integriert in der Bibliothek android.hilt:hilt-android bereit. @ActivityContext ist nur in Modulen verfügbar, die in ActivityComponent installiert sind.
@Binds ist eine effiziente Alternative zu @Provides, wenn eine Methode genau einen Parameter akzeptiert und ihren Typ als Schnittstelle zurückgibt. @Binds generiert einen direkten Cast ohne Methodenaufruf, reduziert die Menge des generierten Codes und verbessert die Injektionsleistung.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch