withContext: co to je, změna kontextu a práce v korutinách

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

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, která mění CoroutineContext pro předaný blok kódu a vrací výsledek
  • Dispatchers.IO — typický argument pro přepnutí na vlákno na pozadí při síťových a diskových operacích
  • Dispatchers.Main — původní kontext, do kterého withContext automaticky vrací provádění po dokončení bloku
  • Sekvenční volání — withContext provádí kód sekvenčně, na rozdíl od launch a async, což zjednodušuje kontrolu nad pořadím operací
  • Výsledek val — withContext vrací hodnotu přímo pomocí return v posledním řádku lambdy, bez await nebo join

Co je withContext v Kotlinu?

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:

kotlin
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.

Klíčová vlastnost: automatický návrat

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.

Kde se withContext používá

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.

Jak funguje withContext: přepínání dispečerů

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í.

Standardní dispečeři pro withContext

DispečerÚčelVelikost fondu
Dispatchers.MainHlavní UI vlákno (Android, JavaFX, Swing)1 (hlavní vlákno)
Dispatchers.IODiskové a síťové operace64 vláken (limit roste)
Dispatchers.DefaultCPU-intenzivní výpočtymax(2, počet jader)
Dispatchers.UnconfinedBez pevného vláknaneomezený

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.

Kdy withContext NEPřepíná vlákno

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.

withContext vs launch a async: kdy co vybrat

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ý.

Porovnání tří funkcí

VlastnostwithContextlaunchasync
Vytváří novou korutinuNeAnoAno
Vrací výsledekAno (T přímo)Ne (Job)Ano (Deferred<T>)
ProváděníSekvenčníParalelníParalelní
Čekání na výsledekAutomatickéjoin()await()
Typické použitíZměna dispečeraFire-and-forgetParalelní výpočty

Pravidlo výběru

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.

Příklady kódu s withContext

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.

Příklad 1: Síťový požadavek v Repository

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í:

kotlin
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.

Příklad 2: Dvě sekvenční operace na pozadí

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:

kotlin
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í.

Příklad 3: Smíšený kontext s NonCancellable

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:

kotlin
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.

Co se děje pod kapotou: Continuation a optimalizace

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.

Jak withContext přepíná kontext na úrovni bytekódu

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.

Optimalizace: fast-path při shodě kontextů

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.

Omezení z hlediska výkonu

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.

Typické chyby při používání 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.

Chyba 1: Vnořené withContext bez potřeby

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.

Chyba 2: Použití withContext místo async pro paralelní úlohy

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.

kotlin
// 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()}")
}

Chyba 3: Zapomenutí na NonCancellable u kritických operací

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.

Chyba 4: Změna stavu UI uvnitř IO bloku

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

Čím se withContext liší od runBlocking?

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.

Lze withContext použít bez suspend?

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.

Co se stane, když do withContext předám stejný dispečer?

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.

Jak withContext pracuje s výjimkami?

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.

Vytváří withContext novou korutinu?

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í

  • withContext — pozastavující funkce pro přepnutí CoroutineContext uvnitř existující korutiny s automatickým návratem do původního kontextu
  • Dispatchers.IO — hlavní dispečer pro síťové požadavky a diskové operace uvnitř withContext
  • Fast-path — optimalizace Kotlin, při které se withContext se stejným dispečerem provádí synchronně bez režie
  • Paralelní úlohy vyžadují async/await, ne withContext — withContext provádí kód sekvenčně
  • NonCancellable — příznak pro kritické operace uvnitř withContext, které nesmí být přerušeny při zrušení korutiny
  • Vrstva Repository — doporučené místo pro withContext v architektuře Android podle průvodců Google
  • Continuation — mechanismus, na kterém je založeno přepínání kontextu v withContext na úrovni bytekódu Kotlin

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é