CoroutineScope — was es ist, Lebenszyklus und Arbeitsweise in Koroutinen

Autor: IT Sectr Veröffentlicht: 2026-06-22 Lesezeit: 10 Min.

CoroutineScope ist ein Kotlin-Interface, das den Lebenszyklus einer Koroutine definiert und einen Kontext zum Starten neuer Koroutinen bereitstellt. Laut der Kotlin-Dokumentation, 2025 enthält jede CoroutineScope-Instanz einen CoroutineContext und verwaltet alle darin gestarteten Koroutinen. Wenn der Scope abgebrochen (cancel) wird, werden alle untergeordneten Koroutinen automatisch abgebrochen, was Speicherlecks verhindert.

Wichtigste Punkte

  • CoroutineScope — ein Interface mit einem einzigen CoroutineContext-Feld, das den Lebenszyklus von Koroutinen definiert
  • Job — ein Kontextelement, das für den Abbruch zuständig ist: Das Abbrechen des Scopes bricht alle untergeordneten Koroutinen ab
  • Strukturierte Nebenläufigkeit — das Prinzip, bei dem untergeordnete Koroutinen an den übergeordneten Scope gebunden sind
  • GlobalScope — ein anwendungsweiter Scope, der aufgrund des Risikos von Speicherlecks nicht empfohlen wird
  • supervisorScope — ein spezieller Scope, bei dem das Abbrechen einer untergeordneten Koroutine die anderen nicht abbricht

Was ist CoroutineScope in Kotlin?

CoroutineScope ist ein fundamentales Interface aus der Bibliothek kotlinx.coroutines, das als Container für Koroutinen dient. Es definiert die Grenzen des Koroutinen-Lebenszyklus: Wenn der Scope abgeschlossen wird, werden alle Koroutinen darin automatisch abgebrochen.

kotlin
public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

Das Interface enthält nur ein Feld — coroutineContext. Darüber stellt der Scope einen Dispatcher, einen Job, einen Ausnahmebehandler und andere Kontextelemente für alle darin gestarteten Koroutinen bereit.

Rolle in der Bibliothek kotlinx.coroutines

Alle Funktionen zum Starten von Koroutinen — launch, async, runBlocking — sind Erweiterungsfunktionen auf CoroutineScope. Das bedeutet, sie können nur aufgerufen werden, wenn ein Scope-Objekt verfügbar ist. Dieses Design stellt sicher, dass jede Koroutine einen klar definierten übergeordneten und Lebenszyklus hat.

Wo CoroutineScope verwendet wird

In Android hat jede Architekturkomponente ihren eigenen Scope: viewModelScope für ViewModel, lifecycleScope für Activity/Fragment. In Serveranwendungen kann ein Scope an eine HTTP-Anfrage oder einen Datenbankverbindungspool gebunden werden.

Wie CoroutineScope funktioniert: Job und strukturierte Nebenläufigkeit

Das Verständnis der inneren Arbeitsweise von CoroutineScope erfordert Vertrautheit mit dem Konzept des Job und dem Prinzip der strukturierten Nebenläufigkeit.

Job — die Aufgabe der Koroutine

Jede Koroutine gibt beim Start ein Job-Objekt (oder Deferred für async) zurück. Job repräsentiert eine Aufgabe mit einem endlichen Lebenszyklus: New, Active, Completing, Completed, Cancelling, Cancelled. Job-Objekte bilden eine Baumstruktur:

  • Übergeordneter Job — der Scope, in dem die Koroutine gestartet wurde
  • Untergeordneter Job — jede Koroutine, die über launch/async gestartet wurde
  • Abbruch des Übergeordneten → Abbruch aller untergeordneten
  • Ausnahme in einem Kind → Abbruch des Übergeordneten (außer in supervisorScope)

Das Prinzip der strukturierten Nebenläufigkeit

Strukturierte Nebenläufigkeit ist ein zentrales Architekturprinzip von Kotlin Coroutines, bei dem der Lebenszyklus der Koroutine an den Lebenszyklus ihres Scopes gebunden ist. Dies steht im Gegensatz zum „Feuer-und-vergiss“-Modell, bei dem eine Koroutine nach Abschluss des Scopes weiterlebt. Vorteile der strukturierten Nebenläufigkeit:

  • Vorhersagbarer Lebenszyklus — wenn der Scope abgeschlossen wird, werden alle Koroutinen garantiert gestoppt
  • Automatische Fehlerbehandlung — eine Ausnahme in einer untergeordneten Koroutine breitet sich zum Scope aus
  • Keine Speicherlecks — keine Koroutine bleibt nach Abschluss des Scopes in Ausführung
  • Klare Hierarchie — der Code spiegelt die logische Struktur paralleler Operationen wider

Lebenszyklus von CoroutineScope

Wenn scope.cancel() aufgerufen wird, wechselt der Job des Scopes in den Zustand Cancelled, was rekursiv alle untergeordneten Jobs abbricht. Nach dem Abbruch kann der Scope nur wiederverwendet werden, wenn eine neue CoroutineScope-Instanz erstellt wird.

Erstellung und Konfiguration von CoroutineScope

Sie können einen CoroutineScope über eine Factory-Funktion oder durch Implementierung des Interfaces in Ihrer Klasse erstellen. Sehen wir uns beide Ansätze an.

Factory-Funktion CoroutineScope()

kotlin
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())

scope.launch {
    println("Running on ${Thread.currentThread().name}")
}

Die Factory-Funktion nimmt einen CoroutineContext entgegen und erstellt einen Scope mit dem angegebenen Kontext. Das Beispiel verwendet Dispatchers.Default für CPU-intensive Aufgaben und SupervisorJob, der Ausnahmen zwischen untergeordneten Koroutinen isoliert.

Implementierung des Interfaces durch Komposition

kotlin
class MyRepository {
    private val scope = CoroutineScope(Dispatchers.IO + Job())

    suspend fun fetchData(): Data = scope.async {
        api.getData()
    }.await()

    fun cleanup() {
        scope.cancel()
    }
}

Wir speichern den Scope als Klassenfeld und rufen manuell cleanup auf, um ihn abzubrechen. Dieser Ansatz eignet sich für Komponenten mit verwaltetem Lebenszyklus — zum Beispiel Repositories oder Manager.

Implementierung durch Delegation

Kotlin ermöglicht die Delegation der CoroutineScope-Implementierung über das Schlüsselwort by:

kotlin
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
    fun load() {
        launch {
            // coroutine runs in DataLoader scope
        }
    }
}

Dieser Ansatz ist praktisch, wenn die Klasse selbst ein Scope ist und Methoden zum Starten von Koroutinen bereitstellen möchte. Seien Sie jedoch vorsichtig: Die Klasse erbt alle Methoden von CoroutineScope, einschließlich cancel, was die Kapselung brechen kann.

GlobalScope vs benutzerdefinierter CoroutineScope

GlobalScope ist ein Singleton-CoroutineScope für die gesamte Anwendung. Seine Verwendung in Produktionscode wird offiziell nicht empfohlen.

Probleme mit GlobalScope

  • Fehlende strukturierte Nebenläufigkeit — Koroutinen in GlobalScope sind nicht an den Komponentenlebenszyklus gebunden
  • Speicherlecks — eine Koroutine kann nach dem Schließen der Activity/Fragment weiter ausgeführt werden
  • Schwierige Tests — GlobalScope kann in Tests nicht ersetzt werden
  • Unkontrollierte Ressourcennutzung — viele Koroutinen können länger als erwartet ausgeführt werden

Wann GlobalScope gerechtfertigt ist

JetBrains erlaubt GlobalScope nur in seltenen Szenarien: anwendungsweite Hintergrundprozesse, die auch nach dem Schließen aller Activities weiterlaufen sollen (z. B. Datensynchronisation, Analytik). Aber selbst in diesen Fällen ist es vorzuziehen, einen eigenen Scope mit CoroutineScope(SupervisorJob()) zu erstellen.

Empfehlung

Verwenden Sie immer einen benutzerdefinierten CoroutineScope mit explizitem Lebenszyklusmanagement. In Android sind dies viewModelScope und lifecycleScope. In Serveranwendungen erstellen Sie einen Scope für jede Anfrage oder jeden Verbindungspool.

coroutineScope vs supervisorScope: Was ist der Unterschied

Beide Funktionen sind Suspend-Funktionen, die einen temporären Scope für parallele Aufgaben erstellen, aber ihr Verhalten bei Ausnahmen unterscheidet sich grundlegend.

MerkmalcoroutineScopesupervisorScope
Verhalten bei FehlernEine Ausnahme in einer untergeordneten Koroutine bricht alle anderen abEine Ausnahme in einer untergeordneten Koroutine bricht die anderen NICHT ab
FehlerweitergabeJa, die erste Ausnahme wird nach außen weitergegebenJa, die erste Ausnahme wird nach außen weitergegeben
Standard-JobJob() — Kinder sind an den Übergeordneten gebundenSupervisorJob() — Kinder sind voneinander unabhängig
Typischer AnwendungsfallAtomare Operation mit mehreren SchrittenUnabhängige parallele Aufgaben (UI-Ladevorgänge)

Wann coroutineScope wählen

Verwenden Sie coroutineScope, wenn mehrere parallele Operationen eine einzige atomare Operation bilden. Zum Beispiel das Laden von Daten von drei Servern: Wenn eine Anfrage fehlschlägt, sind die übrigen sinnlos.

kotlin
suspend fun loadProductPage(): ProductPage = coroutineScope {
    val product = async { api.getProduct() }
    val reviews = async { api.getReviews() }
    ProductPage(product.await(), reviews.await())
}

Wenn getProduct oder getReviews eine Ausnahme auslöst — werden beide Koroutinen abgebrochen und die Ausnahme an den aufrufenden Code weitergegeben.

Wann supervisorScope wählen

Verwenden Sie supervisorScope, wenn parallele Operationen nicht voneinander abhängen. Zum Beispiel das Laden von Profildaten in mehreren unabhängigen Abschnitten: Wenn der Abschnitt mit Empfehlungen fehlschlägt, sollten die Profilkopfzeile und die Freundesliste angezeigt werden.

Häufige Fehler bei der Arbeit mit CoroutineScope

Betrachten wir die häufigsten Entwicklerfehler bei der Verwendung von CoroutineScope in Kotlin.

Fehler 1: Vergessen, den Scope abzubrechen

Das häufigste Szenario für Koroutinen-Leaks ist das Erstellen eines Scopes ohne Aufruf von cancel beim Beenden der Komponente. Wenn der Scope nicht abgebrochen wird, laufen die Koroutinen weiter und halten Referenzen auf Objekte. Verwenden Sie in Android viewModelScope oder lifecycleScope, die automatisch abgebrochen werden.

Fehler 2: Verwendung von GlobalScope in Activity oder Fragment

GlobalScope ignoriert den Android-Komponentenlebenszyklus. Eine in GlobalScope gestartete Koroutine, nachdem eine Activity geschlossen wurde, wird weiter ausgeführt und versucht, die UI zu aktualisieren — was zu einem Absturz führt. Verwenden Sie für UI-Komponenten immer lifecycleScope.

Fehler 3: Wiederverwendung eines abgebrochenen Scopes

Nach dem Aufruf von cancel() kann der Scope nicht wiederverwendet werden — alle Koroutinen darin sind bereits abgeschlossen. Erstellen Sie eine neue CoroutineScope-Instanz über die Factory-Funktion. Job() unterstützt keine Reaktivierung.

Fehler 4: Falsche Delegation des CoroutineScope-Interfaces

Bei der Delegation mit by erhält die Klasse eine öffentliche cancel()-Methode, die von überall aufgerufen werden kann und die Kapselung bricht. Speichern Sie den Scope als privates Feld, anstatt das Interface zu delegieren.

Häufig gestellte Fragen

Was ist der Unterschied zwischen CoroutineScope und CoroutineContext?

CoroutineScope ist ein Interface, das einen CoroutineContext besitzt und für den Lebenszyklus der Koroutinen verantwortlich ist. CoroutineContext ist eine Menge von Elementen (Dispatcher, Job, Fehlerbehandler), die definiert, „wie“ eine Koroutine ausgeführt wird. Ein Unterschied: Der Scope erstellt Koroutinen, während der Kontext ihr Verhalten steuert.

Kann man einen CoroutineScope mit SupervisorJob erstellen?

Ja, das ist ein Standardmuster: CoroutineScope(Dispatchers.IO + SupervisorJob()). SupervisorJob verhindert das kaskadierende Abbrechen untergeordneter Koroutinen, wenn eine von ihnen eine Ausnahme auslöst. Dies ist nützlich für unabhängige parallele Aufgaben, bei denen ein Fehler in einer nicht die anderen stoppen soll.

Wie viele Koroutinen kann ein CoroutineScope enthalten?

Es gibt keine Begrenzung der Anzahl von Koroutinen in einem Scope — sie sind nur durch den verfügbaren Speicher und die Dispatcher-Einstellungen begrenzt. Die praktische Grenze liegt typischerweise bei Tausenden aktiven Koroutinen in einem einzigen Scope. Eine große Anzahl von Koroutinen kann jedoch auf architektonische Probleme hinweisen.

Wie testet man Code mit CoroutineScope?

Der richtige Weg ist, den Scope über den Konstruktor an die Klasse zu übergeben oder runBlockingTest / runTest aus kotlinx-coroutines-test zu verwenden. In Tests können Sie den Scope durch TestCoroutineDispatcher ersetzen und die Koroutinenausführung manuell steuern.

Kann eine Koroutine ihren eigenen Scope haben?

Nein, ein Scope ist ein externer Container für eine Koroutine. Die Koroutine selbst ist kein Scope. Innerhalb einer Koroutine können Sie jedoch einen neuen Scope über coroutineScope oder supervisorScope erstellen, um untergeordnete Koroutinen parallel zu starten.

Zusammenfassung

  • CoroutineScope — ein Interface mit dem Feld coroutineContext, das den Lebenszyklus der darin gestarteten Koroutinen definiert
  • Strukturierte Nebenläufigkeit — das Abbrechen des Scopes bricht automatisch alle untergeordneten Koroutinen ab und verhindert Speicherlecks
  • Job und SupervisorJob — zwei Fehlerbehandlungsmodi: kaskadierender Abbruch (Job) und isolierte Fehler (SupervisorJob)
  • GlobalScope — für die Produktion aufgrund fehlender Lebenszyklusbindung nicht empfohlen
  • coroutineScope vs supervisorScope — atomare parallele Operationen vs unabhängige parallele Aufgaben
  • viewModelScope und lifecycleScope — fertige Scopes für Android, die beim Beenden der Komponente automatisch abgebrochen werden
  • Factory-Funktion — der bevorzugte Weg, einen Scope über CoroutineContext + expliziten cancel-Aufruf zu erstellen

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.

Projekt besprechen

Lesen Sie auch