Koroutinen in Kotlin sind leichtgewichtige Threads mit Unterstützung für das Anhalten und Wiederaufnehmen von Berechnungen, ohne die OS-Threads zu blockieren. Sie werden durch Funktionen wie launch, async innerhalb eines übergeordneten CoroutineScope gestartet.
Die Dispatcher erfolgt über Objekte CoroutineDispatcher, die definieren, in welchem Thread/Executor die Koroutine ausgeführt wird (Dispatchers.Main, Dispatchers.IO, Dispatchers.Default).
Beispiel:
fun main() = runBlocking { launch(Dispatchers.IO) { val data = getDataFromNetwork() withContext(Dispatchers.Main) { updateUI(data) } } }
Abbruch: Koroutinen werden durch den Abbruch ihres Jobs/Scopes abgebrochen, was eine interne Ausnahme CancellationException wirft. Koroutinen sollten regelmäßig das Abbruch-Flag überprüfen oder anhaltende Funktionen aufrufen, um korrekt zu beenden.
Fehlerbehandlung: Ausnahmen in Koroutinen können "untertauchen" — beispielsweise, wenn Sie einen Fehler in einer untergeordneten Koroutine nicht behandeln, wird sie nur ihre Arbeit beenden, und das übergeordnete Element erfährt nicht von dem Problem. Dafür gibt es Mechanismen wie SupervisorJob und CoroutineExceptionHandler.
Feinheiten der Kontextübergabe:
Was passiert, wenn eine der untergeordneten Koroutinen innerhalb der übergeordneten Koroutine mit einem Fehler endet, während andere weiterhin arbeiten? Werden alle untergeordneten abgebrochen?
Viele glauben fälschlicherweise, dass der Fehler "in der untergeordneten bleibt".
Die richtige Antwort: Wenn ein normaler Job (oder launch) verwendet wird, werden alle untergeordneten Koroutinen automatisch beim Fehler einer untergeordneten abgebrochen. Um zu verhindern, dass untergeordnete Koroutinen abgebrochen werden, wird SupervisorJob oder supervisorScope verwendet:
supervisorScope { launch { error("fail") } launch { println("Dieser Code wird ausgeführt") } }
Geschichte
Falsche Fehlerbehandlung — Absturz des Flows: Im Projekt wurden Koroutinen zur Datenladung verwendet. Ausnahmen "tauchten in untergeordneten Koroutinen unter", weshalb die Ebene des Retry/Recovery nicht funktionierte und der Hauptbildschirm leer blieb, ohne irgendwelche Fehlermeldungen für den Benutzer.
Geschichte
Einfrieren der UI aufgrund von Arbeiten mit Dispatchers.Main: Ein junger Android-Entwickler führte eine rechenintensive Aufgabe auf Dispatchers.Main aus — die UI begann zu "einfrieren". Es gab kein Verständnis dafür, dass alle rechenintensiven Berechnungen auf Dispatchers.Default oder Dispatchers.IO ausgeführt werden sollten.
Geschichte
AbortError beim Abbruch des übergeordneten Scopes: Einer der Entwickler hatte nicht berücksichtigt, dass beim Abbruch des übergeordneten Scopes alle untergeordneten Koroutinen durch Abbruch enden, und eine kritische Operation (Daten speichern) nicht abgeschlossen wurde.