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 — ä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.
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.
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.
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.
Förståelse av CoroutineScopes inre struktur kräver kännedom om konceptet Job och principen om strukturerad konkurrens.
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:
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:
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.
CoroutineScope kan skapas via en fabriksfunktion eller genom att implementera gränssnittet i din klass. Låt oss titta på båda metoderna.
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.
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.
Kotlin gör det möjligt att delegera implementeringen av CoroutineScope via nyckelordet by:
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 — är en singleton CoroutineScope för hela applikationen. Dess användning i produktionskod rekommenderas officiellt inte.
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()).
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.
Båda funktionerna är suspend-funktioner som skapar ett tillfälligt scope för parallella uppgifter, men deras beteende vid undantag skiljer sig fundamentalt.
| Egenskap | coroutineScope | supervisorScope |
|---|---|---|
| Beteende vid fel | Undantag i underordnad coroutine avbryter alla andra | Undantag i underordnad coroutine avbryter INTE de andra |
| Felspridning | Ja, första undantaget sprids utåt | Ja, första undantaget sprids utåt |
| Standard Job | Job() — barn är bundna till föräldern | SupervisorJob() — barn är oberoende av varandra |
| Typiskt användningsfall | Atomisk operation av flera steg | Oberoende parallella uppgifter (UI-laddningar) |
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.
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.
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.
Låt oss titta på de vanligaste misstagen som utvecklare gör när de använder CoroutineScope i Kotlin.
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.
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.
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.
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
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.
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.
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.
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.
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
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.
Läs också