Coroutines (Koroutinen) sind leichte Threads in Kotlin für asynchrone Programmierung, verfügbar über die Bibliothek kotlinx.coroutines. Laut JetBrains Kotlin Documentation, 2026 ermöglichen Coroutines das Anhalten der Funktionsausführung ohne Blockieren eines Threads, im Gegensatz zu traditionellen Threads. Koroutinen laufen auf einem begrenzten Thread-Pool, was sie tausendmal leichter macht als native Threads. Kotlin Coroutines sind vollständig in Android Jetpack, Retrofit, Room und andere beliebte Bibliotheken des Android-Ökosystems integriert.
Wichtige Erkenntnisse
Coroutines sind ein asynchroner Programmiermechanismus in Kotlin, implementiert in der Bibliothek kotlinx.coroutines. Im Gegensatz zu Betriebssystem-Threads sind Koroutinen nicht an einen bestimmten Thread gebunden: Sie können in einem Thread pausieren und in einem anderen fortgesetzt werden. Ein einzelner Thread kann Tausende von Koroutinen mit minimalem Overhead ausführen und zwischen ihnen wechseln.
Koroutinen erschienen in Kotlin 1.3 (2018) als experimentelle Funktion und wurden in Kotlin 1.5 (2021) stabil. Coroutines lösen das Callback-Hell-Problem ähnlich wie async/await, bieten aber eine umfangreichere API: Kanäle (Channel), Flow, Ausnahmebehandlung in der Job-Hierarchie und direkte Integration mit Android Lifecycle.
Laut JetBrains (2025) verbraucht jede Koroutine etwa 100 Bytes Speicher gegenüber 1+ MB für einen nativen Thread. Dies ermöglicht das Ausführen von Millionen von Koroutinen in einer einzigen Anwendung ohne Risiko eines OutOfMemoryError. Gerade die Leichtigkeit der Koroutinen macht sie zum bevorzugten Werkzeug für Asynchronität in Android.
Jede Kotlin-Koroutine wird mittels Continuation Passing Style (CPS) in eine Zustandsmaschine kompiliert. Der Compiler fügt jeder suspend-Funktion einen versteckten Continuation-Parameter hinzu. Continuation enthält den Fortsetzungspunkt und alle lokalen Variablen. Wenn eine Koroutine pausiert, speichert die Laufzeitumgebung Continuation, und bei der Fortsetzung wird es auf einem beliebigen verfügbaren Thread aus dem Dispatcher-Pool wiederhergestellt.
suspend ist ein Kotlin-Schlüsselwort, das eine Funktion als anhaltbar markiert. Eine solche Funktion kann nur von einer anderen suspend-Funktion oder von einer Koroutine aus aufgerufen werden. Innerhalb einer suspend-Funktion können Sie andere suspend-Funktionen in beliebiger Reihenfolge aufrufen, und jeder Aufrufpunkt ist ein potenzieller Anhaltepunkt.
Die Mechanik ist einfach: Wenn eine suspend-Funktion eine andere suspend-Funktion aufruft, pausiert sie an diesem Punkt und gibt den Thread frei. Nach Abschluss der aufgerufenen Funktion setzt die Laufzeitumgebung die Ausführung an der gespeicherten Stelle fort. Dies wird als kooperativer Abbruch (cooperative cancellation) bezeichnet — keine Threads werden blockiert.
Wichtig: Eine suspend-Funktion ist standardmäßig nicht asynchron. Die Ausführungsreihenfolge bleibt sequenziell, wenn launch oder async nicht verwendet werden. suspend ermöglicht lediglich, dass die Funktion ohne Thread-Blockierung pausiert und Teil des Koroutinenkontexts sein kann. Continuation Passing Style ist ein Kompilierungsmodell, bei dem jede suspend-Funktion einen versteckten Continuation-Callback erhält und der Compiler eine Zustandsmaschine zur Verwaltung von Pausierungen und Fortsetzungen generiert.
CoroutineScope ist ein Kontext, der den Lebenszyklus von Koroutinen definiert. Alle Koroutinen müssen innerhalb eines Gültigkeitsbereichs gestartet werden. Wenn ein Bereich abgebrochen wird (z.B. bei Beendigung einer Activity), werden alle untergeordneten Koroutinen automatisch abgebrochen. Dies verhindert das Auslaufen von Hintergrundaufgaben. Android Jetpack bietet vorgefertigte Bereiche für jede Komponente: viewModelScope für ViewModel und lifecycleScope für Activity und Fragment, die bei Zerstörung der entsprechenden Komponente automatisch abgebrochen werden.
Strukturierte Nebenläufigkeit (Structured Concurrency) ist ein Prinzip, das garantiert, dass eine Koroutine erst abgeschlossen ist, wenn alle ihre untergeordneten Koroutinen abgeschlossen sind. Die Job-Hierarchie bildet einen Baum: Eine Stammkoroutine erstellt einen übergeordneten Job, Kinder erstellen untergeordnete Jobs. Der Abbruch eines übergeordneten Jobs wird an alle Kinder weitergegeben. Structured Concurrency ist ein grundlegender Unterschied zwischen Koroutinen und Threads.
| Scope | Verwendungsort | Abbruch |
|---|---|---|
| GlobalScope | Nur für Daemon-Aufgaben | Wird nicht automatisch abgebrochen |
| viewModelScope | Android ViewModel | Bei ViewMLöschung |
| lifecycleScope | Android Activity/Fragment | Bei Zerstörung des Lifecycle |
| coroutineScope | Innerhalb von suspend-Funktion | Bei Abbruch des übergeordneten Jobs |
Ein normaler Job bricht alle Geschwister ab, wenn eine untergeordnete Koroutine fehlschlägt. SupervisorJob ist eine Ausnahme: Ein Fehler in einer untergeordneten Koroutine hat keine Auswirkungen auf die anderen. Dies ist wichtig, wenn mehrere unabhängige Aufgaben parallel ausgeführt werden und eine davon ausfallen kann, ohne dass die anderen abgebrochen werden müssen.
Dispatchers legen fest, auf welchen Threads Koroutinen ausgeführt werden. Dispatchers.Main — der Haupt-Android-UI-Thread. Dispatchers.IO — ein Pool für blockierende Operationen (Netzwerk, Festplatte). Dispatchers.Default — für CPU-intensive Aufgaben. Dispatchers.Unconfined — startet im aktuellen Thread, garantiert aber nicht, dort zu bleiben. Die Wahl des richtigen Dispatchers ist entscheidend für die Leistung: Eine IO-Aufgabe auf Default blockiert den Rechenpool, während eine CPU-Aufgabe auf IO unnötige Threads erzeugt.
withContext — eine Funktion zum Wechseln des Dispatchers innerhalb einer Koroutine. Beispielsweise kann eine suspend-Funktion, die JSON parst, für die Berechnung auf Dispatchers.Default umschalten und für UI-Updates auf Dispatchers.Main zurückkehren. withContext ist der am häufigsten verwendete Builder in der Android-Entwicklung.
launch — startet eine Koroutine, gibt einen Job zurück, gibt kein Ergebnis zurück (Fire-and-Forget). async — startet eine Koroutine, gibt ein Deferred zurück, aus dem das Ergebnis über await abgerufen werden kann. runBlocking — blockiert den aktuellen Thread zur Ausführung einer Koroutine (nur für Tests und main-Funktionen). Auswahl des Builders hängt vom Szenario ab: launch eignet sich für Ereignisse und Aktualisierungen, async für Aufgaben mit Ergebnis, runBlocking nur für Tests oder Einstiegspunkte.
Betrachten wir drei praktische Szenarien: eine einfache Koroutine mit launch, einen parallelen Aufruf mit async und die Fehlerbehandlung mit SupervisorJob.
viewModelScope.launch startet eine Koroutine im ViewModel-Kontext. Wenn das ViewModel gelöscht wird, wird die Koroutine automatisch abgebrochen.
class ProfileViewModel : ViewModel() {
fun loadUser() {
viewModelScope.launch(Dispatchers.IO) {
val user = api.fetchUser()
withContext(Dispatchers.Main) {
showUser(user)
}
}
}
}
coroutineScope mit async startet drei Anfragen parallel. Die Ergebnisse werden über .await() gesammelt. Wenn eine Anfrage fehlschlägt, werden alle abgebrochen.
suspend fun loadDashboard(): Dashboard = coroutineScope {
val user = async { api.fetchUser() }
val posts = async { api.fetchPosts() }
val stats = async { api.fetchStats() }
Dashboard(user.await(), posts.await(), stats.await())
}
SupervisorJob ermöglicht jeder Koroutine, unabhängig abzuschließen. Ein Fehler in einer Anfrage bricht die anderen nicht ab.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
try { api.fetchPosts() } catch (e: Exception) { log(e) }
}
Threads sind ein Betriebssystem-Primitiv. Jeder Thread hat seinen eigenen Stack (~1 MB) und benötigt einen Systemaufruf zur Erstellung und zum Wechseln. Koroutinen sind ein Sprach-Primitiv, nicht an das Betriebssystem gebunden. Sie verwenden Continuation zum Speichern des Zustands und wechseln auf Runtime-Ebene ohne Systemaufrufe.
Laut Google (2025) reduziert die Verwendung von Koroutinen anstelle von Threads den Speicherverbrauch für Hintergrundaufgaben in Android-Apps um 90–95%. Alle modernen Android-Bibliotheken (Retrofit, Room, WorkManager) haben integrierte Koroutinenunterstützung über suspend-Funktionen. Ktor (JetBrains HTTP-Client-Framework) ist ebenfalls vollständig auf Koroutinen aufgebaut und bietet suspend-Funktionen für jede Anfrage ohne Callback-API. Room unterstützt Koroutinen über suspend-Funktionen im DAO, was Datenbankabfragen ohne Blockieren des Hauptthreads ermöglicht.
Threads bleiben notwendig für nativen Code über JNI, langlaufende CPU-intensive blockierende Aufrufe (Videorendering, Simulationen) und bei der Integration mit C-Bibliotheken. Für alles andere — Koroutinen.
Häufig gestellte Fragen
Koroutine ist eine anhaltbare Arbeitseinheit, die auf einem vorhandenen Thread ausgeführt wird. Ein Thread ist eine Systemressource mit eigenem Stack. Koroutinen sind tausende Male leichter als Threads und blockieren beim Anhalten keine Ressourcen.
Dispatchers.IO ist für blockierende E/A-Operationen (Netzwerk, Dateien) ausgelegt und kann bei Bedarf neue Threads erstellen. Dispatchers.Default hat einen festen Pool (Anzahl der CPU-Kerne) für CPU-intensive Berechnungen.
Job.cancel() bricht die Koroutine und alle ihre untergeordneten Koroutinen ab. Verwenden Sie ensureActive(), um den Abbruch innerhalb einer Koroutine zu prüfen — es löst eine CancellationException aus, wenn die Koroutine abgebrochen wurde.
Ja — über die Bibliothek kotlinx-coroutines-rx3. Sie stellt Funktionen wie awaitSingle, awaitFirst und andere zur Verfügung, um Observable/Single in suspend-Funktionen und über flowable zurück zu konvertieren.
Flow ist ein kalter asynchroner Datenstrom, das Koroutinen-Äquivalent zu RxJava Observable. Flow gibt Werte sequentiell aus und wird mit einer Ausnahme oder Erfolg abgeschlossen. Es unterstützt map, filter, catch und andere Operatoren.
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