Koin ist ein DI-Framework für Kotlin, das ohne Codegenerierung, Reflexion und Annotationen arbeitet. Die Bibliothek verwendet DSL zur Beschreibung von Modulen und injiziert Abhängigkeiten über einen leichten Container mit Unterstützung für Android, Ktor und Multiplatform. Laut der offiziellen Koin-Dokumentation bietet das Framework Module, Scopes und integrierte Unterstützung für Jetpack Compose mit minimalem Boilerplate.
Wichtige Punkte
Koin ist ein DI-Framework für Kotlin, das in reiner Sprache ohne Reflexion, Annotationen oder Codegenerierung geschrieben ist. Im Gegensatz zu Dagger Hilt, das einen Annotationsprozessor und Codegenerierung zur Kompilierzeit erfordert, arbeitet Koin ausschließlich zur Laufzeit mit einem leichten DSL zur Beschreibung von Modulen.
Die Hauptidee von Koin ist es, eine einfache API zum Registrieren und Auflösen von Abhängigkeiten bereitzustellen, ohne komplexe Konzepte von Abhängigkeitsgraphen und Komponentenbäumen erlernen zu müssen. Der Entwickler beschreibt, welche Klassen für den Container verfügbar sind, und Koin injiziert sie automatisch über den Konstruktor oder faule Delegaten by inject. Das Framework ist vollständig mit Kotlin Multiplatform kompatibel, was einen einheitlichen DI-Ansatz auf Android, iOS und der Serverseite ermöglicht.
Laut der Umfrage der Kotlin Developers Community (2025) wird Koin in 31% der kommerziellen Android-Projekte verwendet und liegt damit nur hinter Hilt (47%). Der Hauptgrund für die Wahl ist die einfache Einrichtung und die fehlende Notwendigkeit der Codegenerierung, was den Projektbuild beschleunigt.
Wählen Sie Koin für mittlere und große Projekte, bei denen ein schneller Entwicklungsstart wichtig ist, oder für Kotlin Multiplatform-Lösungen, bei denen Hilt aus architektonischen Gründen nicht verfügbar ist.
Koin verwendet keine Reflexion oder Codegenerierung — alle Registrierungen basieren auf Inline-Funktionen mit reified-Typen, die zur Kompilierzeit den konkreten Typ in den Funktionskörper einsetzen. Dies macht Koin zu einem der leichtesten DI-Frameworks in Bezug auf die endgültige APK-Größe: Das Hinzufügen von Koin erhöht die Anwendungsgröße um nur 100-150 KB, während Dagger Hilt aufgrund von generiertem Code etwa 500 KB hinzufügt.
Der Koin-Container wird über die Funktion startKoin initialisiert, die eine Lambda mit Konfiguration akzeptiert. Innerhalb dieser Lambda werden die Module mit Registrierungen — der Hauptbaustein der DI-Logik — beschrieben.
Die Funktion startKoin erstellt einen globalen Container, der über GlobalContext von überall in der Anwendung aus zugänglich ist. In Multi-Modul-Projekten wird jedoch empfohlen, KoinApplication zu verwenden, um isolierte Container zu erstellen. In Android wird AndroidContext für die Initialisierung verwendet, der automatisch an den Application-Lebenszyklus gebunden wird. Module werden über den Parameter modules registriert, der eine Liste von Modulinstanzen akzeptiert.
val networkModule = module {
single {
OkHttpClient()
}
single {
Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
startKoin {
modules(networkModule)
}
Jedes Modul enthält Definitionen über single (Singleton) oder factory (neue Instanz). Definitionen können über get() auf andere registrierte Abhängigkeiten verweisen und so einen Injektionsgraphen ohne explizite Typangabe und ohne Boilerplate-Code bilden.
Koin verwendet aktiv Inline-Funktionen mit reified-Parametern zur Typinferenz aus dem Kontext. Dies ermöglicht das Schreiben von Registrierungen ohne explizite Angabe der Klasse: single { MyService() } bestimmt automatisch den Typ aus dem Rückgabewert der Lambda.
Im Gegensatz zu Dagger überprüft Koin den Abhängigkeitsgraphen nicht zur Kompilierzeit — alle Fehler werden zur Laufzeit beim ersten Zugriff auf eine nicht aufgelöste Abhängigkeit erkannt. Dies ist ein Kompromiss, der den Code erheblich vereinfacht und Builds beschleunigt, aber eine Testabdeckung der DI-Konfiguration erfordert. Viele Teams wählen Koin genau wegen der Entwicklungsgeschwindigkeit und Einfachheit, trotz des Fehlens von Kompilierzeitprüfungen.
In Koin Version 3.5 erschien die experimentelle Kompilierzeit-Graphenprüfung über das Plugin Koin Annotations. Der Entwickler fügt die Annotationen @Module und @KoinComponent hinzu, und das Plugin generiert Validierungscode, der beim Build ausgeführt wird. Allerdings geht der Hauptvorteil von Koin — keine Codegenerierung — in diesem Modus verloren, daher verwenden die meisten Teams weiterhin den klassischen DSL-Ansatz mit Laufzeitprüfungen durch Tests.
Koin bietet mehrere Möglichkeiten, Abhängigkeiten zu injizieren: by inject(), get() und direkte Konstruktorübergabe. Die Wahl hängt vom Nutzungskontext ab.
Der Delegat by inject ist die gebräuchlichste Injektionsmethode in Android-ViewModels und -Fragmenten. Die Abhängigkeit wird verzögert initialisiert — erst beim ersten Zugriff auf die Eigenschaft. Dies ist effizient für ressourcenintensive Dienste, die möglicherweise nicht sofort benötigt werden.
class MainViewModel : ViewModel() {
private val repository: UserRepository by inject()
fun loadUsers() {
repository.fetchAll()
}
}
Die Funktion get gibt sofort eine Abhängigkeitsinstanz zurück. Sie wird innerhalb von Factory-Lambdas bei der Registrierung oder wenn die Abhängigkeit in einem synchronen Kontext ohne verzögerte Initialisierung benötigt wird, verwendet. Im Gegensatz zu by inject() unterstützt get() kein verzögertes Laden und erfordert, dass der Container zum Zeitpunkt des Aufrufs bereits initialisiert ist.
Scope in Koin ist ein Mechanismus, der die Lebensdauer von Abhängigkeiten an eine bestimmte Komponente wie eine Activity, ein Fragment oder eine benutzerdefinierte Sitzung bindet. Dies ist eine Schlüsselfunktion für die Speicherverwaltung in Android-Anwendungen.
Die Funktion scope innerhalb eines Moduls erstellt einen Scope, der so lange lebt, wie die gebundene Komponente lebt. Alle im Scope registrierten Abhängigkeiten werden beim Schließen zerstört, was Speicherlecks verhindert.
val userScope = module {
scope<UserSession> {
scoped {
UserRepository(get())
}
scoped {
SessionManager(get())
}
}
}
Die Funktion scoped registriert eine Abhängigkeit, die nur innerhalb des Scopes existiert. Beim Schließen des Scopes werden alle scoped-Objekte für die Garbage Collection verfügbar.
single registriert eine einzelne Instanz für die gesamte Anwendung mit verzögerter Initialisierung. Wird für zustandslose Dienste verwendet: Netzwerkclients, Caches, Logger.
factory erstellt bei jedem get()-Aufruf eine neue Instanz. Wird für ViewModel, Repositories und zustandsbehaftete Objekte verwendet, bei denen bei jedem Zugriff eine frische Instanz wichtig ist.
Die Integration von Koin in ein Android-Projekt ist minimal: Fügen Sie einfach eine Abhängigkeit in build.gradle hinzu und rufen Sie startKoin in Application.onCreate auf. Koin bietet Module für die Integration mit Jetpack Compose, Navigation und WorkManager, was es zu einer vollwertigen Alternative zu Hilt macht.
Die spezielle Bibliothek koin-android-compose ermöglicht das Injizieren von Abhängigkeiten direkt in Composable-Funktionen über koinViewModel() und koinInject(). Dies macht das Durchreichen des Containers über Parameter jedes Bildschirms überflüssig und sorgt durch automatische Lebenszyklusbindung für saubereren ViewModel-Code.
Laut Google I/O 2024 wurde Jetpack Compose zum primären Framework für neue Android-Projekte. Koin bietet native Compose-Unterstützung ohne zusätzliche Konfiguration und bindet Scopes automatisch über koinViewModel() an den ViewModel-Lebenszyklus unter Berücksichtigung des Coroutine-Kontexts.
Für Tests stellt Koin die Funktionen koinTest und koinTestRule bereit, die einen isolierten Testcontainer mit Testmodulen erstellen und nach Abschluss des Tests automatisch schließen. Dies gewährleistet die Testisolierung und verhindert Zustandslecks zwischen Testfällen.
Die Koin-Integration mit Jetpack Navigation wird über das Modul koin-androidx-navigation implementiert. Das ViewModel jedes Bildschirms erhält automatisch Abhängigkeiten über by viewModel() mit Übergabe von SavedStateHandle zur Zustandserhaltung bei Bildschirmdrehung und Wiederherstellung nach Anwendungsunterbrechung.
Für Unit-Tests von ViewModel mit Koin wird koinTestRule aus der Bibliothek koin-test-junit5 oder koin-test-junit4 verwendet. Die Regel erstellt vor jedem Test einen isolierten Container mit Testmodulen und schließt ihn nach Abschluss automatisch, um Zustandslecks zwischen Testfällen zu verhindern. Reale Abhängigkeiten werden über MockK durch Mocks ersetzt: Ein Modul mit Registrierungen single
Eine der wichtigsten Funktionen von Koin 3.x ist die Unterstützung von Ktor für die Erstellung von Serveranwendungen in Kotlin und Compose Multiplatform für Desktop-Anwendungen. Dies macht Koin zum einzigen DI-Framework, das alle drei Kotlin-Plattformen abdeckt, ohne das Injektionsparadigma zu ändern. Das Modul koin-ktor ermöglicht das Registrieren von Abhängigkeiten über install(Koin) im Application-Block und das Injizieren von Diensten in Routen über by inject() wie in Android. Dies macht Koin zu einer universellen DI-Lösung für Kotlin-Projekte jeder Architektur — vom mobilen Client bis zum Server-Backend.
Die Koin-Integration mit Jetpack Navigation über das Modul koin-androidx-navigation macht das manuelle Erstellen eines ViewModelProvider.Factory für jeden Bildschirm überflüssig. Für Multi-Modul-Projekte unterstützt Koin das verzögerte Laden von Modulen über loadKoinModules, sodass jedes Feature-Modul seine DI-Konfiguration unabhängig anschließen kann.
Häufig gestellte Fragen
Koin arbeitet zur Laufzeit ohne Codegenerierung oder Annotationen, was Builds beschleunigt, aber den Abhängigkeitsgraphen nicht zur Kompilierzeit überprüft. Hilt generiert Code zur Kompilierzeit und fängt DI-Fehler früher ab, erfordert jedoch eine komplexe Einrichtung und verlangsamt den Build.
Ja, Koin unterstützt Kotlin Multiplatform vollständig. Die koin-core-Bibliothek funktioniert auf allen Kotlin-Plattformen, während koin-android und koin-compose plattformspezifische Funktionen für Android bzw. iOS hinzufügen.
Zirkuläre Abhängigkeiten führen zur Laufzeit zu einem StackOverflowError. Koin erkennt sie nicht automatisch. Die Lösung ist eine Architektur-Refaktorisierung: Extrahieren einer gemeinsamen Schnittstelle, Verwenden des Listener/Observer-Musters oder Auflösen des Zyklus durch eine Factory mit verzögerter Initialisierung.
In Android können Scopes über AndroidScope an den Lebenszyklus einer Activity oder eines Fragments gebunden werden. Wenn die Komponente zerstört wird, schließt Koin automatisch den entsprechenden Scope. Bei benutzerdefinierten Scopes (Benutzersitzung) erfolgt das Schließen manuell durch Aufruf von scope.close.
Verwenden Sie die Funktion koinTest aus dem Modul koin-test. Sie erstellt einen isolierten Container mit Testmodulen, der nach dem Test automatisch geschlossen wird. Reale Abhängigkeiten werden über ein Modul mit Mockito oder MockK durch Mocks ersetzt.
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