Anwendungsfälle von Kotlin-Coroutines für die Präsentations-/API-/UI-Ebene
Dies ist ein übersetztes Kapitel aus dem Buc Kotlin Coroutines. Wenn Sie mir helfen möchten, die Übersetzung zu verbessern, finden Sie die Quellen auf GitHub.
Die letzte Schicht, die wir besprechen werden, ist die Präsentationsschicht. Hier werden typischerweise Coroutines gestartet. Bei einigen Arten von Anwendungen ist diese Schicht einfacher, da Frameworks wie Spring Boot oder Ktor die gesamte Arbeit für uns erledigen. Zum Beispiel können Sie bei Spring Boot mit Webflux einfach den suspend
Modifikator vor eine Controller-Funktion setzen und Spring wird diese Funktion in einer Coroutine ausführen.
Ähnliche Unterstützung wird von anderen Bibliotheken bereitgestellt. Auf Android verwenden wir Work Manager, um Aufgaben zu planen. Wir können die CoroutineWorker
Klasse nutzen und ihre doWork
Methode implementieren, um festzulegen, was von einer Aufgabe ausgeführt werden soll. Diese Methode ist eine suspend Funktion, also wird sie von der Bibliothek in einer Coroutine gestartet, daher müssen wir dies nicht selbst tun.
Jedoch, in einigen anderen Situationen brauchen wir zu Coroutines selbst starten. Für das verwenden wir typischerweise launch
auf einem Scope-Objekt. Auf Android, dank lifecycle-viewmodel-ktx
, können wir in den meisten Fällen viewModelScope
oder lifecycleScope
benutzen.
Erstellung eines benutzerdefinierten Scopes
Wenn Sie keine Bibliothek oder Klasse zur Verfügung haben, die in der Lage ist, eine Coroutine zu starten oder einen Scope zu erstellen, dann möglicherweise müssen Sie einen eigenen Scope erstellen und ihn nutzen, um eine Coroutine damit zu starten.
Wir definieren einen benutzerdefinierten Coroutine-Bereich mit der CoroutineScope
-Funktion16. Innerhalb davon ist es üblich, SupervisorJob
17 zu verwenden.
Innerhalb einer Scope-Definition könnten wir einen dispatcher oder einen exception handler18 festlegen. Scope-Objekte können auch abgebrochen werden. Tatsächlich werden auf Android die meisten Scopes entweder abgebrochen oder können ihre Kinder unter bestimmten Bedingungen abbrechen. Die Frage "Welchen Scope sollte ich verwenden, um diesen Prozess auszuführen?" kann oft vereinfacht werden zu "Unter welchen Bedingungen sollte dieser Prozess abgebrochen werden?". View-Modelle brechen ihre Scopes ab, wenn sie zerstört werden. WorkManager brechen Scopes ab, wenn die zugehörigen Aufgaben abgebrochen werden.
Nutzung von runBlocking
Anstatt Coroutinen auf einem Scope-Objekt zu starten, können wir auch die Funktion runBlocking
verwenden, die eine Koroutine startet und den aktuellen Thread blockiert, bis diese Koroutine beendet ist. Daher sollte runBlocking
nur verwendet werden, wenn wir einen Thread blockieren wollen. Die zwei häufigsten Gründe für die Verwendung sind:
- Um die
main
Funktion einzuschließen. Dies ist eine korrekte Verwendung vonrunBlocking
, da wir den Thread blockieren müssen, bis die vonrunBlocking
gestartete Koroutine beendet ist. - Um Testfunktionen einzuschließen. In diesem Fall müssen wir auch den Test-Thread blockieren, so dass der Test nicht beendet wird, bis die Koroutine fertig ist.
Beide diese Fälle haben modernere Alternativen. Wir können die Hauptfunktion mit coroutineScope
oder runTest
in Tests aussetzen. Das heißt jedoch nicht, dass wir runBlocking
vermeiden sollten, in einigen Fällen könnte es unseren Anforderungen genügen.
In anderen Situationen, sollten wir runBlocking
vermeiden. Bedenke, dass runBlocking
den aktuellen Thread blockiert, was in Kotlin Coroutines vermieden werden sollte. Verwende runBlocking
nur, wenn du den aktuellen Thread absichtlich blockieren möchtest.
Arbeiten mit Flow
Wenn wir mit Flows arbeiten, behandeln wir oft Änderungen innerhalb von onEach
, wir starten unseren Flow mit launchIn
in einer anderen Coroutine, wir rufen eine Aktion auf, wenn der Flow mit onStart
startet, wir rufen eine Aktion auf, wenn der Flow mit onCompletion
endet, und wir fangen Ausnahmen mit catch
. Wenn wir alle Ausnahmen, die in einem Flow auftreten könnten, fangen wollen, setzen wir catch
an der letzten Position19.
Auf Android ist es beliebt, den Zustand unserer Anwendung in Attributen vom Typ MutableStateFlow
innerhalb von ViewModel-Klassen20 zu repräsentieren. Diese Attribute werden von coroutines beobachtet, die die Ansicht je nach ihren Änderungen aktualisieren.
Wenn eine Eigenschaft, die einen Zustand repräsentiert, nur von einem einzigen Flow abhängt, könnten wir die stateIn
Methode verwenden. Abhängig vom started
Parameter, wird dieser Flow entweder sofort (wenn diese Klasse initialisiert wird), bei Bedarf (wenn die erste Coroutine damit beginnt, diesen zu sammeln), oder während der Abonnementzeit gestartet21.
StateFlow sollte genutzt werden, um einen Zustand zu repräsentieren. Um einige Ereignisse oder Updates von mehreren Coroutines beobachten zu lassen, nutzt man SharedFlow.
Weitere Einzelheiten finden Sie im Kapitel Aufbau eines Coroutine-Bereichs.
Um mehr über die Funktionsweise von SupervisorJob
zu erfahren, siehe das Kapitel Ausnahmebehandlung.
Weitere Einzelheiten finden Sie im Kapitel Aufbau eines Coroutine-Bereichs. Dispatchers und Exception-Handler werden in den Kapiteln Dispatchers und Ausnahmebehandlung beschrieben, jeweils.
Weitere Einzelheiten finden Sie im Kapitel Flow-Lebenszyklusfunktionen.
Weitere Einzelheiten finden Sie im Kapitel SharedFlow und StateFlow.
Alle diese Optionen werden im Kapitel SharedFlow und StateFlow, Unterkapiteln shareIn und stateIn beschrieben.