withContext — funkce přepínající kontext provádění uvnitř korutiny, která dočasně změní vlákno nebo dispečer pro zadaný blok kódu a vrátí výsledek zpět do původního kontextu. Podle údajů JetBrains, 2025 je withContext jedním z nejčastěji používaných nástrojů korutin pro práci s síťovými požadavky a diskovými operacemi. Funkce zaručuje, že po dokončení bloku korutina pokračuje v provádění na původním dispečeru, čímž se předchází náhodným chybám bezpečnosti vláken.
Hlavní body
withContext — pozastavující funkce z balíčku kotlinx.coroutines, která provádí předaný blok kódu v zadaném CoroutineContext a vrací výsledek zpět do původního kontextu. Signatura funkce vypadá takto:
suspend fun withContext (
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
Parametr context přijímá libovolný CoroutineContext — nejčastěji jeden ze standardních Dispatchers.IO, Dispatchers.Default nebo Dispatchers.Main. Blok se provádí přesně v tomto kontextu a výsledek se vrací tam, odkud byl withContext volán.
Po dokončení lambdy withContext zaručeně přepne provádění zpět na původní dispečer. To znamená, že vývojář nemusí ručně volat withContext(Dispatchers.Main) po operaci na pozadí — návrat probíhá automaticky. Toto chování je stanoveno ve specifikaci Kotlin Coroutines od verze 1.3.
Vývoj pro Android — hlavní oblast použití withContext. Typický scénář: ViewModel spouští korutinu na hlavním vlákně, uvnitř se volá withContext(Dispatchers.IO) pro síťový požadavek a výsledek se po automatickém návratu na Main používá k aktualizaci UI. Tento přístup tvoří základ architektury MVVM a je doporučován Googlem v oficiálním průvodci korutinami.
Pro pochopení withContext je třeba se seznámit s CoroutineContext a jeho klíčovou komponentou — dispečerem (Dispatcher). Každá korutina má sadu kontextových prvků, mezi nimiž dispečer určuje, na kterém vlákně nebo fondu vláken se kód provádí.
| Dispečer | Účel | Velikost fondu |
|---|---|---|
| Dispatchers.Main | Hlavní UI vlákno (Android, JavaFX, Swing) | 1 (hlavní vlákno) |
| Dispatchers.IO | Diskové a síťové operace | 64 vláken (limit roste) |
| Dispatchers.Default | CPU-intenzivní výpočty | max(2, počet jader) |
| Dispatchers.Unconfined | Bez pevného vlákna | neomezený |
Je důležité pochopit, že withContext nevytváří novou korutinu — pouze mění kontext pro existující. To je klíčový rozdíl oproti launch a async, které vytvářejí nové korutiny. Vnitřní implementace withContext je optimalizována: pokud se požadovaný kontext shoduje s aktuálním, k přepnutí nedochází — funkce se provádí na stejném dispečeru.
Dispatchers.Main uvnitř withContext(Dispatchers.Main) nezpůsobuje přepnutí — Kotlin Coroutines rozpozná identitu kontextů a přeskočí zbytečnou operaci. Podobně withContext(Dispatchers.Default) uvnitř korutiny již běžící na Default nevytváří režii. Tato optimalizace je implementována v ContinuationInterceptor.
Začátečníci často zaměňují withContext s launch a async, protože všechny tři funkce pracují s korutinami a kontextem. Jejich účel je však zásadně odlišný.
| Vlastnost | withContext | launch | async |
|---|---|---|---|
| Vytváří novou korutinu | Ne | Ano | Ano |
| Vrací výsledek | Ano (T přímo) | Ne (Job) | Ano (Deferred<T>) |
| Provádění | Sekvenční | Paralelní | Paralelní |
| Čekání na výsledek | Automatické | join() | await() |
| Typické použití | Změna dispečera | Fire-and-forget | Paralelní výpočty |
Pokud potřebujete provést jednu operaci na vlákně na pozadí a získat výsledek — použijte withContext. Pokud potřebujete spustit několik nezávislých operací paralelně — použijte async s await. Pokud výsledek není potřeba (logování, zápis cache) — launch. Google doporučuje withContext jako preferovaný nástroj pro vrstvu Repository v architektuře Android.
Podívejme se na tři praktické scénáře použití withContext v Android aplikacích v Kotlinu. Každý příklad ukazuje konkrétní úkol a správný vzor.
ViewModel volá metodu repozitáře z korutiny na Main. Uvnitř withContext(Dispatchers.IO) se provádí HTTP požadavek a výsledek se automaticky vrací:
class UserRepository(
private val api: UserApi
) {
suspend fun getUser(id: String): User {
return withContext(Dispatchers.IO) {
api.fetchUser(id)
}
}
}
Korutina ve ViewModelu volá getUser stejně jako běžnou pozastavující funkci — bez explicitního uvedení dispečera. withContext skrývá detaily přepínání vláken.
Když je třeba provést několik IO operací jednu po druhé, withContext je spojí do jednoho bloku. To je efektivnější než zabalit každou operaci do samostatného withContext:
suspend fun loadUserProfile(id: String): Profile {
return withContext(Dispatchers.IO) {
val user = api.fetchUser(id)
val posts = api.fetchPosts(id)
Profile(user, posts)
}
}
Obě operace se provádějí na Dispatchers.IO a výsledek Profile se vytváří a vrací bez zbytečných přepnutí kontextu. Pokud jsou operace nezávislé, je lepší použít async pro paralelní provádění.
V některých scénářích je třeba provést kód, který nelze zrušit — například uložení stavu při zavírání obrazovky. Kombinace withContext + NonCancellable řeší tento úkol:
withContext(Dispatchers.IO + NonCancellable) {
cache.saveState(state)
analytics.logEvent("state_saved")
}
Operátor + kombinuje dva kontextové prvky: IO dispečer a příznak NonCancellable. Blok se provádí, i když byla nadřazená korutina zrušena — to je užitečné pro finalizační operace.
Vnitřní implementace withContext se opírá o mechanismus Continuation — centrální abstrakci korutin Kotlin. Každý bod pozastavení (suspend point) ukládá stav provádění do objektu Continuation a withContext není výjimkou.
Kotlin překladač překládá withContext na volání metody withContext z kotlinx.coroutines, která interně vytváří novou instanci DispatchedContinuation. Tento objekt obaluje původní Continuation a nahrazuje v něm dispečera. Pokud se nový dispečer liší od aktuálního, provádění je pozastaveno, blok je odeslán do příslušného fondu vláken a po dokončení — obnoveno s původním kontextem.
Když je withContext volán se stejným dispečerem, na kterém již korutina běží, Kotlin aktivuje fast-path: blok se provádí synchronně, bez vytváření DispatchedContinuation a bez odesílání do fondu vláken. To činí withContext prakticky zdarma při opakovaných voláních se stejným kontextem. Podle benchmarků JetBrains (kotlinx.coroutines 1.8) se fast-path provádí za méně než 0,1 μs.
Každé volání withContext s jiným dispečerem vytváří nový DispatchedContinuation a vyžaduje přepnutí vlákna — to trvá 1 až 5 μs v závislosti na zatížení. Pro většinu aplikací je toto zpoždění nepostřehnutelné, ale ve smyčkách s tisíci iteracemi je lepší agregovat operace do jednoho bloku withContext.
I zkušení vývojáři dělají chyby při práci s withContext. Podívejme se na čtyři nejčastější problémy a způsoby jejich prevence.
Vývojáři často balí každý řádek do samostatného withContext, místo aby spojili operace do jednoho bloku. Každé další volání s jiným dispečerem vytváří režii.
Správně: spojte sekvenční IO operace do jednoho withContext(Dispatchers.IO) { ... }. Pokud je část operací CPU-intenzivní — použijte withContext(Dispatchers.Default) uvnitř stejného bloku.
withContext provádí kód sekvenčně. Pokud jsou dva nezávislé síťové požadavky zabaleny do jednoho withContext, budou prováděny jeden po druhém. Pro paralelismus použijte async + await.
// Sekvenční — pomalé
withContext(Dispatchers.IO) {
val a = api.fetchA()
val b = api.fetchB()
}
// Paralelní — rychlé
coroutineScope {
val a = async { api.fetchA() }
val b = async { api.fetchB() }
println("${a.await()} ${b.await()}")
}
Pokud je korutina zrušena během withContext, blok na Dispatchers.IO je také přerušen. Pro operace, které musí být dokončeny za každou cenu (zápis do databáze, odeslání analytiky), kombinujte withContext s NonCancellable.
Nikdy neaktualizujte View komponenty uvnitř withContext(Dispatchers.IO). withContext se nevrací na Main až do dokončení celého bloku. Aktualizaci UI proveďte po uzavírací závorce withContext — tehdy bude korutina již na hlavním vlákně.
Často kladené otázky
withContext — pozastavující funkce, která neblokuje vlákno, ale přepíná kontext uvnitř existující korutiny. runBlocking — most mezi korutinami a běžným kódem, který blokuje aktuální vlákno až do dokončení. withContext je bezpečný pro UI vlákno, runBlocking — ne.
Ne, withContext je pozastavující funkce, takže ji lze volat pouze z jiné pozastavující funkce nebo z korutiny (launch/async). Z běžné funkce withContext volat nelze — k tomu je potřeba runBlocking nebo CoroutineScope.
Kotlin aktivuje fast-path — blok se provádí synchronně na stejném vlákně bez přepnutí. Režie je menší než 0,1 μs. Není to chyba, ale takové volání je nadbytečné — kód je lepší provést bez withContext.
Výjimky uvnitř withContext se šíří stejně jako v běžném kódu — přes try-catch. Pokud blok vyhodí výjimku, šíří se do nadřazené korutiny a ruší ji, pokud není ošetřena. Použijte try-catch uvnitř withContext nebo kolem něj.
Ne, withContext nevytváří novou korutinu. Používá stávající korutinu, ale dočasně mění její kontext. To ho odlišuje od launch a async, které vytvářejí podřízené korutiny. Chování je potvrzeno zdrojovým kódem kotlinx.coroutines.
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é