CoroutineScope — vad är det, livsområde och funktion i coroutines

Författare: IT Sectr Publicerad: 2026-06-22 Lästid: 10 min

CoroutineScope — är ett Kotlin-gränssnitt som definierar coroutinens livsomfång och tillhandahåller kontext för att starta nya coroutines. Enligt Kotlin-dokumentationen, 2025, innehåller varje CoroutineScope-instans en CoroutineContext och hanterar alla coroutines som startats i den. När scopet avslutas (cancel) avbryts alla underordnade coroutines automatiskt, vilket förhindrar minnesläckor.

Huvudpunkter

  • CoroutineScope — gränssnitt med ett enda fält CoroutineContext, som definierar coroutinernas livscykel
  • Job — kontextelement som ansvarar för avbrytning: avbrytning av scopet avbryter alla underordnade coroutines
  • Strukturerad konkurrens — princip där underordnade coroutines är bundna till det överordnade scopet
  • GlobalScope — scope för hela applikationen, som inte rekommenderas på grund av läckagerisk
  • supervisorScope — speciellt scope där avbrytning av en underordnad coroutine inte avbryter de andra

Vad är CoroutineScope i Kotlin?

CoroutineScope — är ett grundläggande gränssnitt från kotlinx.coroutines-biblioteket som fungerar som en behållare för coroutines. Det definierar livsgränserna för coroutines: när scopet avslutas avbryts alla coroutines inuti det automatiskt.

kotlin
public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

Gränssnittet innehåller bara ett fält — coroutineContext. Genom detta tillhandahåller scopet en dispatcher (Dispatcher), uppgift (Job), undantagshantering och andra kontextelement för alla coroutines som startats i det.

Roll i kotlinx.coroutines-biblioteket

Alla funktioner för att starta coroutines — launch, async, runBlocking — är tilläggsfunktioner på CoroutineScope. Detta innebär att de bara kan anropas när ett scope-objekt finns. En sådan design garanterar att varje coroutine har en väldefinierad förälder och livscykel.

Var tillämpas CoroutineScope

I Android har varje arkitekturkomponent sitt eget scope: viewModelScope för ViewModel, lifecycleScope för Activity/Fragment. I serverapplikationer kan scopet kopplas till en HTTP-förfrågan eller till en anslutningspool till databasen.

Hur fungerar CoroutineScope: Job och strukturerad konkurrens

Förståelse av CoroutineScopes inre struktur kräver kännedom om konceptet Job och principen om strukturerad konkurrens.

Job — coroutinens uppgift

Varje coroutine returnerar vid start ett Job-objekt (eller Deferred för async). Job representerar en uppgift med en definierad livscykel: New, Active, Completing, Completed, Cancelling, Cancelled. Job-objekt bildar en trädstruktur:

  • Överordnat Job — scopet där coroutinen startades
  • Underordnat Job — varje coroutine startad via launch/async
  • Avbrytning av förälder → avbrytning av alla barn
  • Undantag i barn → avbrytning av förälder (förutom supervisorScope)

Principen om strukturerad konkurrens

Strukturerad konkurrens — den viktigaste arkitekturprincipen i Kotlin Coroutines, där coroutinens livslängd är bunden till dess scopes livslängd. Detta står i kontrast till modellen “fire-and-förget”, där en coroutine fortsätter att leva efter att scopet avslutats. Fördelar med strukturerad konkurrens:

  • Förutsägbar livscykel — när scopet avslutas stoppas alla coroutines garanterat
  • Automatisk felhantering — undantag i en underordnad coroutine sprids till scopet
  • Inga läckor — ingen coroutine förblir aktiv efter scopets slut
  • Tydlig hierarki — koden reflekterar den logiska strukturen av parallella operationer

CoroutineScopes livscykel

När scope.cancel() anropas går scopets Job till status Cancelled, vilket rekursivt avbryter alla underordnade Job. Efter avbrytning kan scopet endast återanvändas genom att skapa en ny CoroutineScope-instans.

Skapa och konfigurera CoroutineScope

CoroutineScope kan skapas via en fabriksfunktion eller genom att implementera gränssnittet i din klass. Låt oss titta på båda metoderna.

Fabriksfunktion CoroutineScope()

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

scope.launch {
    println("Kör på ${Thread.currentThread().name}")
}

Fabriksfunktionen accepterar CoroutineContext och skapar ett scope med angiven kontext. I exemplet används Dispatchers.Default för CPU-intensiva uppgifter och SupervisorJob som isolerar undantag mellan underordnade coroutines.

Implementering av gränssnitt via komposition

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

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

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

Vi lagrar scopet som ett fält i klassen och anropar manuellt cleanup för att avbryta det. Detta lämpar sig för komponenter med hanterad livscykel — till exempel för repositories eller chefer.

Implementering via delegering

Kotlin gör det möjligt att delegera implementeringen av CoroutineScope via nyckelordet by:

kotlin
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
    fun load() {
        launch {
            // coroutinen kör i DataLoader-scope
        }
    }
}

Denna metod är praktisk när klassen själv är ett scope och vill tillhandahålla metoder för att starta coroutines. Var dock försiktig: klassen ärver alla CoroutineScope-metoder, inklusive cancel, vilket kan bryta inkapslingen.

GlobalScope vs anpassad CoroutineScope

GlobalScope — är en singleton CoroutineScope för hela applikationen. Dess användning i produktionskod rekommenderas officiellt inte.

Problem med GlobalScope

  • Brist på strukturerad konkurrens — coroutines i GlobalScope är inte bundna till komponentens livscykel
  • Minnesläckor — en coroutine kan fortsätta köra efter att Activity/Fragment stängts
  • Svårt att testa — GlobalScope kan inte ersättas i tester
  • Okontrollerad resursförbrukning — många coroutines kan köra längre än förväntat

När är GlobalScope berättigat

JetBrains tillåter GlobalScope endast i sällsynta scenarier: bakgrundsprocesser på applikationsnivå som måste leva även efter att alla Activity stängts (till exempel datasynkronisering, analys). Men även i dessa fall är det bättre att skapa ett eget scope med CoroutineScope(SupervisorJob()).

Rekommendation

Använd alltid anpassad CoroutineScope med explicit livscykelhantering. I Android är dessa viewModelScope och lifecycleScope. I serverapplikationer, skapa ett scope för varje förfrågan eller anslutningspool.

coroutineScope vs supervisorScope: vad är skillnaden

Båda funktionerna är suspend-funktioner som skapar ett tillfälligt scope för parallella uppgifter, men deras beteende vid undantag skiljer sig fundamentalt.

EgenskapcoroutineScopesupervisorScope
Beteende vid felUndantag i underordnad coroutine avbryter alla andraUndantag i underordnad coroutine avbryter INTE de andra
FelspridningJa, första undantaget sprids utåtJa, första undantaget sprids utåt
Standard JobJob() — barn är bundna till föräldernSupervisorJob() — barn är oberoende av varandra
Typiskt användningsfallAtomisk operation av flera stegOberoende parallella uppgifter (UI-laddningar)

När välja coroutineScope

Använd coroutineScope när flera parallella operationer utgör en enda atomisk operation. Till exempel att ladda data från tre servrar: om en förfrågan misslyckas är de andra meningslösa.

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

Om getProduct eller getReviews kastar ett undantag — avbryts båda coroutines och undantaget sprids till anropande kod.

När välja supervisorScope

Använd supervisorScope när parallella operationer inte är beroende av varandra. Till exempel att ladda profildata i flera oberoende sektioner: om rekommendationssektionen misslyckas bör profilrubriken och vänlistan visas.

Vanliga misstag vid arbete med CoroutineScope

Låt oss titta på de vanligaste misstagen som utvecklare gör när de använder CoroutineScope i Kotlin.

Misstag 1: Glömde att avbryta scopet

Det vanligaste scenariot för coroutine-läckor — att skapa ett scope utan att anropa cancel när komponenten avslutas. Om scopet inte avbryts fortsätter coroutines att köra och håller referenser till objekt. I Android, använd viewModelScope eller lifecycleScope, som avbryts automatiskt.

Misstag 2: Använda GlobalScope i Activity eller Fragment

GlobalScope ignorerar livscykeln för Android-komponenter. En coroutine som startats i GlobalScope efter att Activity stängts fortsätter att köra och försöker uppdatera UI — vilket leder till en krasch. Använd alltid lifecycleScope för UI-komponenter.

Misstag 3: Återanvända ett avbrutet scope

Efter att cancel() anropats kan scopet inte återanvändas — alla coroutines i det är redan slutförda. Skapa en ny CoroutineScope-instans via fabriksfunktionen. Job() stödjer inte återaktivering.

Misstag 4: Felaktig delegering av CoroutineScope-gränssnittet

Vid delegering med by får klassen en publik metod cancel() som kan anropas var som helst ifrån, vilket bryter inkapslingen. Förvara scopet som ett privat fält, delegera inte gränssnittet.

Vanliga frågor

Vad är skillnaden mellan CoroutineScope och CoroutineContext?

CoroutineScope — är ett gränssnitt som äger CoroutineContext och är ansvarigt för coroutinernas livscykel. CoroutineContext — är en uppsättning element (dispatcher, job, felhantering) som definierar „hur” en coroutine utförs. En av skillnaderna: scopet skapar coroutines, context hanterar deras beteende.

Kan man skapa CoroutineScope med SupervisorJob?

Ja, detta är ett standardmönster: CoroutineScope(Dispatchers.IO + SupervisorJob()). SupervisorJob förhindrar kaskadartad avbrytning av underordnade coroutines när ett undantag uppstår i en av dem. Detta är användbart för oberoende parallella uppgifter där ett fel i en inte bör stoppa de andra.

Hur många coroutines kan CoroutineScope innehålla?

Det finns inga begränsningar för antalet coroutines i ett scope — de begränsas endast av tillgängligt minne och dispatcher-inställningar. Den praktiska gränsen är vanligtvis tusentals aktiva coroutines i ett enda scope. Ett stort antal coroutines kan dock indikera arkitekturproblem.

Hur testar man kod med CoroutineScope?

Rätt sätt är att skicka scopet till klassen via konstruktorn eller använda runBlockingTest / runTest från kotlinx-coroutines-test. I tester kan du ersätta scopet med TestCoroutineDispatcher och manuellt styra coroutinernas exekvering.

Kan en coroutine ha sitt eget scope?

Nej, scopet är en extern behållare för coroutinen. Själva coroutinen är inte ett scope. Inuti en coroutine kan dock ett nytt scope skapas via coroutineScope eller supervisorScope för parallell start av underordnade coroutines.

Sammanfattning

  • CoroutineScope — gränssnitt med fältet coroutineContext, som definierar livscykeln för coroutines som startats i det
  • Strukturerad konkurrens — avbrytning av scopet avbryter automatiskt alla underordnade coroutines, vilket förhindrar minnesläckor
  • Job och SupervisorJob — två fellägen: kaskadartad avbrytning (Job) och isolerade fel (SupervisorJob)
  • GlobalScope — rekommenderas inte för produktion på grund av bristande koppling till livscykeln
  • coroutineScope vs supervisorScope — atomiska parallella operationer vs oberoende parallella uppgifter
  • viewModelScope och lifecycleScope — färdiga scopes för Android, avbryts automatiskt när komponenten avslutas
  • Fabriksfunktion — föredragen metod för att skapa scope via CoroutineContext + explicit anrop av cancel

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också