withContext egy végrehajtási kontextust váltó függvény a korutinon belül, amely ideiglenesen megváltoztatja a szálat vagy diszpécsert a megadott kódblokk számára, és visszaadja az eredményt az eredeti kontextusba. A JetBrains, 2025 adatai szerint a withContext az egyik leggyakrabban használt korutin eszköz a hálózati kérések és lemez műveletek során. A függvény garantálja, hogy a blokk befejezése után a korutina az eredeti diszpécseren folytatja a végrehajtást, megelőzve a véletlen szálbiztonsági hibákat.
Főbb pontok
withContext egy felfüggesztő függvény a kotlinx.coroutines csomagból, amely a megadott kódblokkot egy meghatározott CoroutineContext-ben hajtja végre, és visszaadja az eredményt az eredeti kontextusba. A függvény aláírása így néz ki:
suspend fun withContext (
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
A context paraméter bármilyen CoroutineContext-et elfogad — leggyakrabban a szabványos Dispatchers.IO, Dispatchers.Default vagy Dispatchers.Main egyikét. A blokk pontosan ebben a kontextusban hajtódik végre, és az eredmény oda kerül vissza, ahonnan a withContext-et hívták.
A lambda befejezése után a withContext garantáltan visszakapcsolja a végrehajtást az eredeti diszpécserre. Ez azt jelenti, hogy a fejlesztőnek nem kell manuálisan meghívnia a withContext(Dispatchers.Main) függvényt egy háttérművelet után — a visszatérés automatikusan megtörténik. Ezt a viselkedést a Kotlin Coroutines specifikációja az 1.3-as verzió óta rögzíti.
Android fejlesztés — a withContext fő alkalmazási területe. Tipikus forgatókönyv: a ViewModel elindít egy korutint a főszálon, belül meghívja a withContext(Dispatchers.IO) függvényt egy hálózati kéréshez, és az eredményt az automatikus Main-re való visszatérés után használja a UI frissítéséhez. Ez a megközelítés képezi az MVVM architektúra alapját, és a Google ajánlja a hivatalos korutin útmutatóban.
A withContext megértéséhez meg kell ismerkednie a CoroutineContext-tel és annak kulcsfontosságú összetevőjével — a diszpécserrel (Dispatcher). Minden korutinának van egy kontextelem-készlete, amelyek között a diszpécser határozza meg, hogy melyik szálon vagy szálkészleten hajtódik végre a kód.
| Diszpécser | Rendeltetés | Készlet mérete |
|---|---|---|
| Dispatchers.Main | Fő UI szál (Android, JavaFX, Swing) | 1 (fő szál) |
| Dispatchers.IO | Lemez és hálózati műveletek | 64 szál (a korlát nő) |
| Dispatchers.Default | CPU-intenzív számítások | max(2, magok száma) |
| Dispatchers.Unconfined | Rögzített szál nélkül | korlátlan |
Fontos megérteni, hogy a withContext nem hoz létre új korutint — csak megváltoztatja a kontextust a meglévő számára. Ez a legfontosabb különbség a launch és async függvényekhez képest, amelyek új korutinokat hoznak létre. A withContext belső implementációja optimalizált: ha a kért kontextus megegyezik a jelenlegivel, a váltás nem történik meg — a függvény ugyanazon a diszpécseren hajtódik végre.
Dispatchers.Main a withContext(Dispatchers.Main) belsejében nem okoz váltást — a Kotlin Coroutines felismeri a kontextusok azonosságát, és kihagyja a felesleges műveletet. Hasonlóképpen, a withContext(Dispatchers.Default) egy már Default-on futó korutin belül nem hoz létre többletterhelést. Ez az optimalizálás a ContinuationInterceptor-ban van implementálva.
A kezdők gyakran összekeverik a withContext-et a launch és async függvényekkel, mivel mindhárom függvény korutinokkal és kontextussal dolgozik. Azonban a céljuk alapvetően különbözik.
| Jellemző | withContext | launch | async |
|---|---|---|---|
| Új korutint hoz létre | Nem | Igen | Igen |
| Visszaadja az eredményt | Igen (T közvetlenül) | Nem (Job) | Igen (Deferred<T>) |
| Végrehajtás | Szekvenciális | Párhuzamos | Párhuzamos |
| Eredményre várás | Automatikus | join() | await() |
| Tipikus use-case | Diszpécser váltása | Fire-and-forget | Párhuzamos számítások |
Ha egy műveletet kell végrehajtania egy háttérszálon és eredményt kell kapnia — használja a withContext-et. Ha több független műveletet kell párhuzamosan elindítania — használja az async-et await-tel. Ha nincs szükség az eredményre (naplózás, gyorsítótár írás) — launch. A Google a withContext-et ajánlja előnyben részesített eszközként a Repository rétegben az Android architektúrában.
Nézzünk meg három gyakorlati forgatókönyvet a withContext használatára Android alkalmazásokban Kotlin nyelven. Minden példa egy konkrét feladatot és a helyes mintát mutatja be.
A ViewModel meghívja a repository metódust egy Main-en futó korutinból. Belül a withContext(Dispatchers.IO) végrehajtja a HTTP-kérést, és az eredmény automatikusan visszakerül:
class UserRepository(
private val api: UserApi
) {
suspend fun getUser(id: String): User {
return withContext(Dispatchers.IO) {
api.fetchUser(id)
}
}
}
A ViewModel-beli korutin a getUser függvényt ugyanúgy hívja, mint egy szokásos felfüggesztő függvényt — anélkül, hogy explicit módon megadná a diszpécsert. A withContext elrejti a szálváltás részleteit.
Amikor több IO-műveletet kell egymás után végrehajtani, a withContext egyetlen blokkba egyesíti őket. Ez hatékonyabb, mint minden műveletet külön withContext-be csomagolni:
suspend fun loadUserProfile(id: String): Profile {
return withContext(Dispatchers.IO) {
val user = api.fetchUser(id)
val posts = api.fetchPosts(id)
Profile(user, posts)
}
}
Mindkét művelet a Dispatchers.IO-n hajtódik végre, és a Profile eredmény felesleges kontextusváltás nélkül jön létre és kerül visszaadásra. Ha a műveletek függetlenek, jobb az async használata a párhuzamos végrehajtáshoz.
Bizonyos forgatókönyvekben olyan kódot kell végrehajtani, amely nem szakítható meg — például állapot mentése a képernyő bezárásakor. A withContext + NonCancellable kombinációja megoldja ezt a feladatot:
withContext(Dispatchers.IO + NonCancellable) {
cache.saveState(state)
analytics.logEvent("state_saved")
}
A + operátor két kontextelem kombinálja: az IO diszpécsert és a NonCancellable zászlót. A blokk akkor is végrehajtódik, ha a szülő korutin megszakításra került — ez hasznos a véglegesítő műveletekhez.
A withContext belső implementációja a Continuation mechanizmusra épül — a Kotlin korutinok központi absztrakciójára. Minden felfüggesztési pont (suspend point) elmenti a végrehajtás állapotát egy Continuation objektumba, és a withContext sem kivétel.
A Kotlin fordító a withContext-et a kotlinx.coroutines withContext metódusának hívásává fordítja, amely belül létrehoz egy új DispatchedContinuation példányt. Ez az objektum becsomagolja az eredeti Continuation-t és lecseréli benne a diszpécsert. Ha az új diszpécser eltér a jelenlegitől, a végrehajtás felfüggesztésre kerül, a blokk elküldésre kerül a megfelelő szálkészletbe, és befejezés után — az eredeti kontextussal folytatódik.
Amikor a withContext ugyanazzal a diszpécserrel kerül meghívásra, amelyen a korutin már fut, a Kotlin fast-path-et aktivál: a blokk szinkron módon, DispatchedContinuation létrehozása és szálkészletbe küldés nélkül hajtódik végre. Ez gyakorlatilag ingyenessé teszi a withContext-et ismételt hívásoknál ugyanazzal a kontextussal. A JetBrains benchmarkok szerint (kotlinx.coroutines 1.8) a fast-path kevesebb mint 0,1 μs alatt végrehajtódik.
Minden withContext hívás eltérő diszpécserrel új DispatchedContinuation-t hoz létre és szálváltást igényel — ez a terheléstől függően 1-5 μs-ig tart. A legtöbb alkalmazás számára ez a késleltetés észrevehetetlen, de ezres iterációszámú ciklusokban érdemes a műveleteket egyetlen withContext blokkba összegyűjteni.
Még tapasztalt fejlesztők is követnek el hibákat a withContext használatakor. Nézzünk meg négy leggyakoribb problémát és azok megelőzési módjait.
A fejlesztők gyakran minden sort külön withContext-be csomagolnak, ahelyett, hogy a műveleteket egyetlen blokkba egyesítenék. Minden további hívás eltérő diszpécserrel többletterhelést hoz létre.
Helyesen: egyesítse a szekvenciális IO műveleteket egy withContext(Dispatchers.IO) { ... } blokkba. Ha a műveletek egy része CPU-intenzív — használjon withContext(Dispatchers.Default) függvényt ugyanazon a blokkon belül.
A withContext szekvenciálisan hajtja végre a kódot. Ha két független hálózati kérés van egy withContext-be csomagolva, egymás után fognak végrehajtódni. Párhuzamosításhoz használja az async + await kombinációt.
// Szekvenciális — lassú
withContext(Dispatchers.IO) {
val a = api.fetchA()
val b = api.fetchB()
}
// Párhuzamos — gyors
coroutineScope {
val a = async { api.fetchA() }
val b = async { api.fetchB() }
println("${a.await()} ${b.await()}")
}
Ha a korutin megszakításra kerül a withContext alatt, a Dispatchers.IO-n lévő blokk is megszakad. Azoknál a műveleteknél, amelyeknek mindenképpen be kell fejeződniük (adatbázisba írás, analitika küldése), kombinálja a withContext-et a NonCancellable-lel.
Soha ne frissítse a View komponenseket a withContext(Dispatchers.IO) blokkon belül. A withContext csak a teljes blokk befejezése után tér vissza a Main-re. A UI frissítést a withContext záró kapcsos zárójele után végezze el — akkor a korutin már a főszálon lesz.
Gyakran Ismételt Kérdések
withContext — felfüggesztő függvény, amely nem blokkolja a szálat, hanem kontextust vált a meglévő korutinon belül. A runBlocking — híd a korutinok és a szokásos kód között, amely blokkolja az aktuális szálat a befejezésig. A withContext biztonságos a UI szál számára, a runBlocking — nem.
Nem, a withContext egy felfüggesztő függvény, ezért csak egy másik felfüggesztő függvényből vagy korutinból (launch/async) hívható. Egy szokásos függvényből a withContext nem hívható — ehhez runBlocking vagy CoroutineScope szükséges.
A Kotlin aktiválja a fast-path-et — a blokk szinkron módon, ugyanazon a szálon hajtódik végre váltás nélkül. A többletterhelés kevesebb, mint 0,1 μs. Ez nem hiba, de az ilyen hívás felesleges — jobb, ha egyszerűen végrehajtja a kódot withContext nélkül.
A withContext-en belüli kivételek ugyanúgy terjednek, mint a szokásos kódban — a try-catch segítségével. Ha a blokk kivételt dob, az a szülő korutinba terjed, és ha nincs kezelve, megszakítja azt. Használjon try-catch-et a withContext-en belül vagy körülötte.
Nem, a withContext nem hoz létre új korutint. A meglévő korutint használja, de ideiglenesen megváltoztatja annak kontextusát. Ez különbözteti meg a launch és async függvényektől, amelyek gyermek korutinokat hoznak létre. A viselkedést a kotlinx.coroutines forráskódja is megerősíti.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is