Dispatchers v Kotlin Coroutines — komponenty CoroutineContext, které určují vlákna pro provádění korutin: Main (UI vlákno), IO (síť a disk), Default (CPU-intenzivní úlohy) a Unconfined (aktuální vlákno). Každý dispečer spravuje specializovaný fond vláken optimalizovaný pro konkrétní typ práce. Podle průvodce JetBrains, 2024 je výběr správného dispečera kritický pro výkon a stabilitu aplikace.
Hlavní body
Dispatchers — implementace rozhraní CoroutineDispatcher, které jsou prvky CoroutineContext. Určují, na kterém vlákně nebo fondu vláken bude korutina provedena. Při vytváření korutiny pomocí launch nebo async může být dispečer předán jako první parametr: launch(Dispatchers.IO) { ... }. Pokud není dispečer zadán, dědí se z vnějšího CoroutineScope.
Kotlin poskytuje čtyři vestavěné dispečery: Main, IO, Default, Unconfined. Každý dispečer používá svůj vlastní fond vláken optimalizovaný pro konkrétní typ operací. Správný výběr dispečera určuje výkon aplikace: chybný výběr vede k zpomalení rozhraní, nevyužití jader CPU nebo neefektivnímu spotřebování vláken.
| Dispečer | Fond vláken | Max. vláken | Použití |
|---|---|---|---|
| Dispatchers.Main | Jedno (UI) | 1 | Aktualizace UI, LiveData, View |
| Dispatchers.IO | Fond IO | 64 (limitedParallelism) | Síť, soubory, databáze |
| Dispatchers.Default | Fond CPU | N jader | Řazení, parsování, výpočty |
| Dispatchers.Unconfined | Aktuální vlákno | N/A | Mezioperační úkoly, testy |
Dispatchers.Main — dispečer provádějící korutiny na hlavním vlákně Androidu. Je určen pro operace související s rozhraním: aktualizace TextView, volání notifyDataSetChanged, práce s LiveData a StateFlow. V Androidu je tento dispečer implementován pomocí Handler (Looper.getMainLooper()).
// Správné přepnutí na Main pro aktualizace UI
viewModelScope.launch(Dispatchers.IO) {
val data = repository.fetchData()
withContext(Dispatchers.Main) {
_uiState.value = data
}
}
Pokud je korutina již na dispečeru Main, dodatečný withContext(Dispatchers.Main) nevytváří režii — dispečer zkontroluje aktuální vlákno a přepnutí přeskočí. withContext je preferovaný způsob přepínání mezi dispečery.
Dispatchers.IO — dispečer optimalizovaný pro vstupně-výstupní operace: HTTP požadavky (Ktor, OkHttp), čtení a zápis souborů, práce s Room nebo SQLDelight. Ve výchozím nastavení používá fond 64 vláken, škálovatelný pod zátěží. Každý nový IO požadavek může vytvořit další vlákno až do dosažení limitu.
Pro řízení počtu současných IO operací použijte limitedParallelism(). Tato funkce vytvoří nový dispečer s omezením počtu paralelních vláken, čímž zabraňuje vyčerpání fondu při hromadných operacích.
val limitedIo = Dispatchers.IO.limitedParallelism(4)
// Načíst 100 souborů s limitem 4 souběžných operací
coroutineScope {
val files = (1..100).map { index ->
async(limitedIo) {
downloadFile("file_$index")
}
}
files.awaitAll()
}
Používejte IO-dispečer pro všechny operace, kde korutina tráví čas čekáním (I/O-bound). CPU-intenzivní úlohy na IO-dispečeru jsou neefektivní — zabírají vlákna určená pro vstup-výstup, čímž snižují propustnost systému.
Dispatchers.Default — dispečer pro výpočetní operace zatěžující procesor: řazení, filtrování, parsování JSON (Moshi, Kotlinx Serialization), zpracování obrazu, výpočty. Velikost fondu se rovná počtu jader procesoru (nejméně však 2). To zajišťuje maximální zatížení CPU bez přepínání kontextu.
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
return withContext(Dispatchers.Default) {
input
.parallelStream()
.map { transform(it) }
.toList()
}
}
Nepoužívejte Dispatchers.Default pro IO operace — zablokuje to vlákna CPU fondu, která by mohla zpracovávat výpočetní úlohy. Rozdělení na IO a Default umožňuje optimálně využívat systémové prostředky: IO vlákna čekají na vstup-výstup, CPU vlákna jsou neustále zaměstnána výpočty.
Dispatchers.Unconfined — speciální dispečer, který nepřipojuje korutinu k žádnému fondu. Korutina začíná provádění ve stejném vlákně, kde byla volána launch/async, a po pozastavení pokračuje ve vlákně, které volalo resume. Toto chování je vhodné pro mezioperační úkoly, které nevyžadují pevný kontext.
fun main() = runBlocking {
launch(Dispatchers.Unconfined) {
println("Před zpožděním: ${Thread.currentThread().getName()}")
delay(500L)
println("Po zpoždění: ${Thread.currentThread().getName()}")
}
}
V produkčním kódu se Dispatchers.Unconfined používá zřídka. Hlavní případy: lehké transformace před předáním dat jinému dispečerovi a testy. Pro produkční zatížení používejte explicitní dispečery — Unconfined je nepředvídatelný, protože vlákno provedení závisí na implementaci resume.
Výběr dispečera je určen typem úlohy: UI operace → Main, I/O-bound → IO, CPU-bound → Default, mezioperační → dědění z scope. Pro Android se doporučuje spustit korutinu na tom dispečerovi, kde se provádí hlavní práce, a před aktualizací UI přepnout na Main pomocí withContext.
Pro složité scénáře kombinujte dispečery pomocí operátoru +: Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler. Tím vytvoříte CoroutineContext s určeným dispečerem, zpracováním chyb a izolovanou hierarchií Job.
Často kladené otázky
Dispatchers.IO používá fond až 64 vláken pro I/O-bound operace (čekání na vstup-výstup), zatímco Dispatchers.Default — fond podle počtu jader CPU pro výpočetní úlohy. Při nedostatku vláken si oba fondy mohou vzájemně půjčovat vlákna.
Ano, použijte newSingleThreadContext() pro jednovláknový nebo newFixedThreadPoolContext() pro pevný fond. Pro produkci aplikujte limitedParallelism() na základě stávajících dispečerů — je to efektivnější než vytváření nových fondů.
Pokud Dispatchers.Main není k dispozici (např. v JUnit testu nebo službě na pozadí), je vyvolána výjimka IllegalStateException. Pro testy použijte TestCoroutineDispatcher, pro služby na pozadí — Dispatchers.IO nebo Default.
Použijte Dispatchers.IO.limitedParallelism(N), kde N je maximální počet paralelních vláken. Tím zabráníte vyčerpání fondu při hromadných požadavcích a získáte řízený paralelismus.
Dispatchers.Unconfined je vhodný pro mezioperační úkoly: lehké transformace dat před předáním jinému dispečerovi, testovací scénáře. V produkčním kódu Androidu se nedoporučuje kvůli nejistému vláknu provedení po pozastavení.
Shrnutí
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í.
Přečtěte si také