Coroutine Builder — Funktionen von Kotlin Coroutines, die Coroutinen erstellen und starten und deren Ausführungsweise bestimmen. Die Builder launch, async, runBlocking und produce decken verschiedene Szenarien ab: von Hintergrundaufgaben bis hin zu parallelen Berechnungen mit Rückgabewert. Laut JetBrains, 2024 ist Coroutine Builder die Grundlage des Coroutinen-Modells und bietet strukturierte Nebenläufigkeit und Lebenszyklusverwaltung.
Wichtige Punkte
Coroutine Builder ist eine Kotlin-Erweiterungsfunktion, die einen CoroutineScope und einen suspend-Block entgegennimmt und eine neue Coroutine erstellt und startet. Jeder Builder definiert, wie die Coroutine ausgeführt wird: mit oder ohne Ergebnisrückgabe, mit Thread-Blockierung oder asynchron. Builder sind die Einstiegspunkte in das Coroutinen-Modell der Sprache.
Alle Builder arbeiten über CoroutineScope, das den Lebenszyklus untergeordneter Coroutinen verwaltet. Wenn ein Bereich abgebrochen wird, werden alle darüber gestarteten Coroutinen automatisch abgebrochen — dies ist das Prinzip der strukturierten Nebenläufigkeit. Dieser Ansatz verhindert Coroutinen-Lecks und garantiert einen vorhersagbaren Abschluss.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Builder laufen innerhalb von CoroutineScope
val job = launch {
delay(1000L)
println("Welt!")
}
println("Hallo,")
job.join()
}
Kotlin bietet vier integrierte Coroutine-Builder: launch, async, runBlocking und produce. Jeder hat seinen eigenen Rückgabetyp und Anwendungsbereich. Für die mobile Android-Entwicklung sind launch und async die wichtigsten Builder — sie arbeiten nicht-blockierend und integrieren sich in Architekturkomponenten.
| Builder | Rückgabetyp | Thread-Blockierung | Szenario |
|---|---|---|---|
| launch | Job | Nein | Fire-and-Forget-Aufgaben |
| async | Deferred<T> | Nein | Parallele Berechnungen |
| runBlocking | T | Ja | Tests, main-Funktion |
| produce | ReceiveChannel<E> | Nein | Streaming (veraltet) |
Jeder Builder akzeptiert zusätzliche Parameter: CoroutineStart (Startstrategie), CoroutineContext (Dispatcher, Ausnahmen) und einen benannten Codeblock. Standardmäßig startet die Coroutine sofort (CoroutineStart.DEFAULT).
launch ist der am häufigsten verwendete Builder in der Android-Entwicklung. Er startet eine Coroutine, die kein Ergebnis zurückgibt, und gibt ein Job-Objekt zur Verwaltung ihres Lebenszyklus zurück. Dies ist die ideale Wahl für Operationen, die nur einen Nebeneffekt benötigen: Datenbank speichern, Analysen senden, UI aktualisieren.
Der launch-Builder akzeptiert CoroutineScope, einen optionalen CoroutineContext und einen suspend-Block. Das zurückgegebene Job-Objekt ermöglicht es, die Coroutine abzubrechen, auf ihren Abschluss zu warten oder ihren Status zu überprüfen.
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
val data = fetchFromNetwork()
saveToDatabase(data)
}
job.start()
job.join()
Der Parameter CoroutineStart.LAZY verschiebt die Ausführung bis zum expliziten Aufruf von start() oder join(). Dies ist nützlich für verzögerte Initialisierung und bedingte Ausführung. Für die standardmäßige sofortige Ausführung wird CoroutineStart.DEFAULT verwendet oder der Parameter weggelassen.
async ist ein Builder, der Deferred<T> zurückgibt — ein asynchrones Versprechen eines Ergebnisses. Der Aufruf von await() setzt die Coroutine aus, bis das Ergebnis vorliegt, ohne den Thread zu blockieren. Dies ist der primäre Mechanismus für parallele Aufgaben in Kotlin-Coroutinen.
async ist besonders effektiv, wenn mehrere unabhängige Operationen gleichzeitig ausgeführt werden müssen. Im Gegensatz zu sequenziellen Aufrufen von suspend-Funktionen startet async Coroutinen parallel und reduziert so die Gesamtausführungszeit.
suspend fun fetchUserData(): UserData {
val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
return UserData(
profile = deferred1.await(),
settings = deferred2.await(),
notifications = deferred3.await()
)
}
Deferred erbt von Job, daher unterstützt async alle Lebenszyklusoperationen: Abbruch, Warten auf Abschluss, Ausnahmebehandlung. Wenn ein Bereich abgebrochen wird, werden untergeordnete Deferred-Coroutinen automatisch abgebrochen.
runBlocking ist der einzige Builder, der den aktuellen Thread blockiert, bis die Coroutine abgeschlossen ist. Er erstellt einen neuen CoroutineScope und startet die übergebene Coroutine, wobei der aufrufende Thread blockiert wird. Er wird in main()-Einstiegspunkten, in Tests und bei der Integration mit blockierendem Code verwendet.
runBlocking ist in drei Szenarien gerechtfertigt: dem Anwendungseinstiegspunkt (main), Komponententests von suspend-Funktionen und der Integration mit Callback-basierten Bibliotheken, wo suspend nicht verwendet werden kann. In Produktions-Android-Code wird die Verwendung von runBlocking im Hauptthread dringend nicht empfohlen.
class CoroutineTest {
@Test
fun `test suspend function`() = runBlocking {
val result = mySuspendFunction()
assertEquals("expected", result)
}
}
Für Tests wird empfohlen, kotlinx-coroutines-test mit TestCoroutineDispatcher anstelle von runBlocking zu verwenden — dies bietet Zeitkontrolle und vermeidet Blockierungen in der Testumgebung.
Die Wahl des Coroutine Builders hängt vom zurückgegebenen Ergebnis und dem Ausführungsszenario ab. Wenn die Operation keine Daten zurückgeben muss — verwenden Sie launch. Wenn Sie das Ergebnis einer asynchronen Operation benötigen — verwenden Sie async. runBlocking sollte nur zum Überbrücken verwendet werden, und ersetzen Sie produce durch Flow für reaktive Streams.
In Android-Projekten mit Kotlin Coroutines sind die wichtigsten Builder launch und async. launch wird in ViewModel und UseCases verwendet, um Coroutinen zu starten, während async für parallele Anfragen an das Netzwerk oder die Datenbank verwendet wird. Moderne Bibliotheken (Ktor, Room) unterstützen bereits suspend-Funktionen, was die Notwendigkeit der direkten Verwendung von async minimiert.
Häufig gestellte Fragen
launch gibt Job zurück und kein Ausführungsergebnis, während async Deferred<T> zurückgibt — ein Objekt, aus dem das Ergebnis über await() abgerufen werden kann. launch wird für Fire-and-Forget-Operationen verwendet, async für Aufgaben, die Daten zurückgeben.
Nicht empfohlen. runBlocking im Hauptthread verursacht ANR und blockiert die UI. Verwenden Sie lifecycleScope.launch innerhalb von Activity und Fragment — dies ist eine integrierte Lösung ohne Blockierung.
Der launch-Builder gibt ein Job-Objekt zurück, das die Steuerung des Coroutinen-Lebenszyklus ermöglicht: abbrechen (cancel), auf Abschluss warten (join), Status prüfen (isActive, isCompleted, isCancelled).
Deferred<T> ist ein asynchrones Versprechen eines Ergebnisses, das vom async-Builder zurückgegeben wird. Es erbt von Job und fügt Methoden hinzu: await() zum Abrufen des Ergebnisses, getCompleted() für nicht-blockierenden Zugriff und getCompletionExceptionOrNull() zur Überprüfung von Ausnahmen.
Verwenden Sie den Parameter CoroutineStart.LAZY: scope.launch(start = CoroutineStart.LAZY) { ... }. Rufen Sie dann job.start() oder job.join() für die tatsächliche Ausführung auf. Dies ist nützlich für verzögerte Initialisierung und bedingte Coroutinen-Ausführung.
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