CoroutineScope — co to je, oblast života a fungování v korutinách

Autor: IT Sectr Publikováno: 2026-06-22 Doba čtení: 10 min

CoroutineScope — je rozhraní Kotlin, které definuje oblast života korutiny a poskytuje kontext pro spouštění nových korutin. Podle dokumentace Kotlin, 2025, každá instance CoroutineScope obsahuje CoroutineContext a spravuje všechny korutiny v ní spuštěné. Když scope skončí (cancel), všechny podřízené korutiny jsou automaticky zrušeny, což zabraňuje únikům paměti.

Hlavní body

  • CoroutineScope — rozhraní s jediným polem CoroutineContext, které definuje životní cyklus korutin
  • Job — prvek kontextu odpovědný za zrušení: zrušení scope zruší všechny podřízené korutiny
  • Strukturální konkurence — princip, při kterém jsou podřízené korutiny vázány na nadřízený scope
  • GlobalScope — scope pro celou aplikaci, který se nedoporučuje kvůli riziku úniků
  • supervisorScope — speciální scope, ve kterém zrušení jedné podřízené korutiny nezruší ostatní

Co je CoroutineScope v Kotlinu?

CoroutineScope — je základní rozhraní z knihovny kotlinx.coroutines, které slouží jako kontejner pro korutiny. Definuje hranice života korutin: když scope skončí, všechny korutiny uvnitř jsou automaticky zrušeny.

kotlin
public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

Rozhraní obsahuje pouze jedno pole — coroutineContext. Prostřednictvím něj scope poskytuje dispečera (Dispatcher), úkol (Job), zpracování výjimek a další prvky kontextu pro všechny korutiny v něm spuštěné.

Role v knihovně kotlinx.coroutines

Všechny funkce pro spouštění korutin — launch, async, runBlocking — jsou rozšiřující funkce na CoroutineScope. To znamená, že je lze volat pouze při existenci objektu scope. Takový návrh zaručuje, že každá korutina má jasně definovaného rodiče a životní cyklus.

Kde se CoroutineScope používá

V Androidu má každá architektonická komponenta svůj scope: viewModelScope pro ViewModel, lifecycleScope pro Activity/Fragment. V serverových aplikacích může být scope vázán na HTTP požadavek nebo na fond připojení k databázi.

Jak funguje CoroutineScope: Job a strukturální konkurence

Porozumění vnitřní struktuře CoroutineScope vyžaduje znalost konceptu Job a principu strukturální konkurence.

Job — úkol korutiny

Každá korutina při spuštění vrátí objekt Job (nebo Deferred pro async). Job představuje úkol s definovaným životním cyklem: New, Active, Completing, Completed, Cancelling, Cancelled. Objekty Job tvoří stromovou strukturu:

  • Rodičovský Job — scope, ve kterém byla korutina spuštěna
  • Podřízený Job — každá korutina spuštěná přes launch/async
  • Zrušení rodiče → zrušení všech dětí
  • Výjimka v dítěti → zrušení rodiče (kromě supervisorScope)

Princip strukturální konkurence

Strukturální konkurence — klíčový architektonický princip Kotlin Coroutines, při kterém je životnost korutiny vázána na životnost jejího scope. To je v kontrastu s modelem „fire-and-forget”, kde korutina žije dále po skončení scope. Výhody strukturální konkurence:

  • Předvídatelný životní cyklus — když scope skončí, všechny korutiny jsou garantovaně zastaveny
  • Automatické zpracování chyb — výjimka v libovolné podřízené korutině se šíří na scope
  • Žádné úniky — žádná korutina nezůstává aktivní po skončení scope
  • Jasná hierarchie — kód odráží logickou strukturu paralelních operací

Životní cyklus CoroutineScope

Když je zavoláno scope.cancel(), Job scope přejde do stavu Cancelled, což rekurzivně zruší všechny podřízené Job. Po zrušení lze scope znovu použít pouze vytvořením nové instance CoroutineScope.

Vytvoření a konfigurace CoroutineScope

CoroutineScope lze vytvořit pomocí tovární funkce nebo implementací rozhraní ve vlastní třídě. Podíváme se na oba přístupy.

Tovární funkce CoroutineScope()

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

scope.launch {
    println("Běží na ${Thread.currentThread().name}")
}

Tovární funkce přijímá CoroutineContext a vytváří scope s uvedeným kontextem. V příkladu je použit Dispatchers.Default pro úkoly náročné na CPU a SupervisorJob, který izoluje výjimky mezi podřízenými korutinami.

Implementace rozhraní pomocí kompozice

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

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

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

Ukládáme scope jako pole třídy a ručně voláme cleanup pro jeho zrušení. To je vhodné pro komponenty s řízeným životním cyklem — například pro repozitáře nebo správce.

Implementace pomocí delegování

Kotlin umožňuje delegovat implementaci CoroutineScope pomocí klíčového slova by:

kotlin
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
    fun load() {
        launch {
            // korutina běží v scope DataLoader
        }
    }
}

Takový přístup je vhodný, když třída sama je scope a chce poskytovat metody spouštění korutin. Buďte však opatrní: třída dědí všechny metody CoroutineScope, včetně cancel, což může porušit zapouzdření.

GlobalScope vs vlastní CoroutineScope

GlobalScope — je singleton CoroutineScope pro celou aplikaci. Jeho použití v produkčním kódu se oficiálně nedoporučuje.

Problémy GlobalScope

  • Nedostatek strukturální konkurence — korutiny v GlobalScope nejsou vázány na životní cyklus komponenty
  • Úniky paměti — korutina může pokračovat v běhu po zavření Activity/Fragment
  • Obtížné testování — GlobalScope nelze nahradit v testech
  • Nekontrolovaná spotřeba zdrojů — mnoho korutin může běžet déle, než se očekává

Kdy je GlobalScope ospravedlnitelný

JetBrains povoluje GlobalScope pouze ve výjimečných scénářích: procesy na pozadí na úrovni aplikace, které musí být živé i po zavření všech Activity (například synchronizace dat, analytika). Ale i v těchto případech je lepší vytvořit vlastní scope s CoroutineScope(SupervisorJob()).

Doporučení

Vždy používejte vlastní CoroutineScope s explicitním řízením životního cyklu. V Androidu jsou to viewModelScope a lifecycleScope. V serverových aplikacích vytvářejte scope pro každý požadavek nebo fond připojení.

coroutineScope vs supervisorScope: jaký je rozdíl

Obě funkce jsou suspend funkce, které vytvářejí dočasný scope pro paralelní úkoly, ale jejich chování při výjimkách se zásadně liší.

CharakteristikacoroutineScopesupervisorScope
Chování při chyběVýjimka v podřízené korutině zruší všechny ostatníVýjimka v podřízené korutině NEzruší ostatní
Šíření chybyAno, první výjimka je šířena venAno, první výjimka je šířena ven
Výchozí JobJob() — děti jsou vázány na rodičeSupervisorJob() — děti nezávisí na sobě
Typický případ použitíAtomická operace z více krokůNezávislé paralelní úkoly (UI načítání)

Kdy zvolit coroutineScope

Použijte coroutineScope, když několik paralelních operací tvoří jedinou atomickou operaci. Například načítání dat ze tří serverů: pokud jeden požadavek selže, ostatní nemají smysl.

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

Pokud getProduct nebo getReviews vyhodí výjimku — obě korutiny jsou zrušeny a výjimka je šířena do volajícího kódu.

Kdy zvolit supervisorScope

Použijte supervisorScope, když paralelní operace nezávisí na sobě. Například načítání dat profilu ve více nezávislých sekcích: pokud sekce doporučení selže, záhlaví profilu a seznam přátel by se měly zobrazit.

Typické chyby při práci s CoroutineScope

Podíváme se na nejčastější chyby vývojářů při používání CoroutineScope v Kotlinu.

Chyba 1: Zapomněli jste zrušit scope

Nejčastější scénář úniku korutiny — vytvoření scope bez volání cancel při ukončení komponenty. Pokud scope není zrušen, korutiny pokračují v běhu a drží reference na objekty. V Androidu použijte viewModelScope nebo lifecycleScope, které se ruší automaticky.

Chyba 2: Použití GlobalScope v Activity nebo Fragmentu

GlobalScope ignoruje životní cyklus Android komponent. Korutina spuštěná v GlobalScope po zavření Activity bude pokračovat v běhu a pokusí se aktualizovat UI — což povede k pádu. Vždy používejte lifecycleScope pro UI komponenty.

Chyba 3: Opětovné použití zrušeného scope

Po volání cancel() nelze scope znovu použít — všechny korutiny v něm jsou již dokončeny. Vytvořte novou instanci CoroutineScope pomocí tovární funkce. Job() nepodporuje opětovnou aktivaci.

Chyba 4: Nesprávné delegování rozhraní CoroutineScope

Při delegování pomocí by třída získá veřejnou metodu cancel(), kterou lze zavolat odkudkoli, což porušuje zapouzdření. Uchovávejte scope jako soukromé pole, nedelegujte rozhraní.

Často kladené otázky

Čím se liší CoroutineScope od CoroutineContext?

CoroutineScope — je rozhraní, které vlastní CoroutineContext a je odpovědné za životní cyklus korutin. CoroutineContext — je sada prvků (dispečer, job, zpracování chyb), která určuje „jak“ se korutina provádí. Jedním z rozdílů je, že scope vytváří korutiny, context řídí jejich chování.

Lze vytvořit CoroutineScope s SupervisorJob?

Ano, toto je standardní vzor: CoroutineScope(Dispatchers.IO + SupervisorJob()). SupervisorJob zabraňuje kaskádovému zrušení podřízených korutin při výjimce v jedné z nich. To je užitečné pro nezávislé paralelní úkoly, kde chyba v jednom by neměla zastavit ostatní.

Kolik korutin může CoroutineScope obsahovat?

Neexistují omezení počtu korutin v scope — jsou omezeny pouze dostupnou pamětí a nastavením dispečera. Praktický limit bývá obvykle tisíce aktivních korutin v jednom scope. Velký počet korutin však může naznačovat architektonické problémy.

Jak testovat kód s CoroutineScope?

Správným způsobem je předat scope třídě prostřednictvím konstruktoru nebo použít runBlockingTest / runTest z kotlinx-coroutines-test. V testech můžete scope nahradit TestCoroutineDispatcher a ručně řídit provádění korutin.

Může mít jedna korutina vlastní scope?

Ne, scope je vnější kontejner pro korutinu. Samotná korutina není scope. Uvnitř korutiny však lze vytvořit nový scope pomocí coroutineScope nebo supervisorScope pro paralelní spouštění podřízených korutin.

Shrnutí

  • CoroutineScope — rozhraní s polem coroutineContext, které definuje životní cyklus v něm spuštěných korutin
  • Strukturální konkurence — zrušení scope automaticky ruší všechny podřízené korutiny, čímž zabraňuje únikům paměti
  • Job a SupervisorJob — dva režimy zpracování chyb: kaskádové zrušení (Job) a izolované chyby (SupervisorJob)
  • GlobalScope — nedoporučuje se pro produkci kvůli chybějící vazbě na životní cyklus
  • coroutineScope vs supervisorScope — atomické paralelní operace vs nezávislé paralelní úkoly
  • viewModelScope a lifecycleScope — hotové scopes pro Android, automaticky rušené při ukončení komponenty
  • Tovární funkce — preferovaný způsob vytvoření scope pomocí CoroutineContext + explicitní volání cancel

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také