withContext är en funktion som växlar exekveringskontexten inuti en korutin, tillfälligt ändrar tråden eller avsändaren för ett angivet kodblock och returnerar resultatet tillbaka till den ursprungliga kontexten. Enligt data från JetBrains, 2025 är withContext ett av de mest använda verktygen för korutiner vid nätverksförfrågningar och diskoperationer. Funktionen garanterar att efter blockets slutförande fortsätter korutinen på den ursprungliga avsändaren, vilket förhindrar oavsiktliga trådsäkerhetsfel.
Huvudpunkter
withContext är en avstängningsfunktion från paketet kotlinx.coroutines som utför det angivna kodblocket i en specificerad CoroutineContext och returnerar resultatet tillbaka till den ursprungliga kontexten. Funktionssignaturen ser ut så här:
suspend fun withContext (
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
Parametern context accepterar vilken CoroutineContext som helst — oftast en av standard Dispatchers.IO, Dispatchers.Default eller Dispatchers.Main. Blocket utförs precis i denna kontext och resultatet returneras dit där withContext anropades.
Efter lambdans slutförande växlar withContext garanterat tillbaka till den ursprungliga avsändaren. Detta innebär att utvecklaren inte behöver anropa withContext(Dispatchers.Main) manuellt efter en bakgrundsoperation — återgången sker automatiskt. Detta beteende fastställdes i specifikationen för Kotlin Coroutines från och med version 1.3.
Android-utveckling — det huvudsakliga tillämpningsområdet för withContext. Typiskt scenario: ViewModel startar en korutin på huvudtråden, inuti anropas withContext(Dispatchers.IO) för en nätverksförfrågan och resultatet efter automatisk återgång till Main används för att uppdatera UI. Detta tillvägagångssätt ligger till grund för MVVM-arkitekturen och rekommenderas av Google i den officiella guiden om korutiner.
För att förstå withContext måste du känna till CoroutineContext och dess nyckelkomponent — avsändaren (Dispatcher). Varje korutin har en uppsättning kontextelement, bland vilka avsändaren bestämmer på vilken tråd eller trådpool koden utförs.
| Avsändare | Ändamål | Poolstorlek |
|---|---|---|
| Dispatchers.Main | Huvud UI-tråd (Android, JavaFX, Swing) | 1 (huvudtråd) |
| Dispatchers.IO | Disk- och nätverksoperationer | 64 trådar (gränsen växer) |
| Dispatchers.Default | CPU-intensiva beräkningar | max(2, antal kärnor) |
| Dispatchers.Unconfined | Utan fast tråd | obegränsad |
Det är viktigt att förstå att withContext inte skapar en ny korutin — det endast ändrar kontexten för den befintliga. Detta är den viktigaste skillnaden jämfört med launch och async, som skapar nya korutiner. Den interna implementeringen av withContext är optimerad: om den begärda kontexten matchar den nuvarande sker ingen växling — funktionen utförs på samma avsändare.
Dispatchers.Main inuti withContext(Dispatchers.Main) orsakar ingen växling — Kotlin Coroutines känner igen kontexternas identitet och hoppar över den onödiga operationen. På samma sätt skapar withContext(Dispatchers.Default) inuti en korutin som redan kör på Default ingen overhead. Denna optimering är implementerad i ContinuationInterceptor.
Nybörjare förväxlar ofta withContext med launch och async, eftersom alla tre funktionerna arbetar med korutiner och kontext. Deras syfte är dock fundamentalt olika.
| Egenskap | withContext | launch | async |
|---|---|---|---|
| Skapar ny korutin | Nej | Ja | Ja |
| Returnerar resultat | Ja (T direkt) | Nej (Job) | Ja (Deferred<T>) |
| Utförande | Sekventiellt | Parallellt | Parallellt |
| Vänta på resultat | Automatiskt | join() | await() |
| Typiskt användningsområde | Byte av avsändare | Fire-and-forget | Parallella beräkningar |
Om du behöver utföra en operation på en bakgrundstråd och få resultatet — använd withContext. Om du behöver starta flera oberoende operationer parallellt — använd async med await. Om resultatet inte behövs (loggning, cache-skrivning) — launch. Google rekommenderar withContext som det föredragna verktyget för Repository-lagret i Android-arkitekturen.
Låt oss titta på tre praktiska scenarier för användning av withContext i Android-appar i Kotlin. Varje exempel visar en specifik uppgift och rätt mönster.
ViewModel anropar repository-metoden från en korutin på Main. Inuti utför withContext(Dispatchers.IO) HTTP-förfrågan och resultatet returneras automatiskt:
class UserRepository(
private val api: UserApi
) {
suspend fun getUser(id: String): User {
return withContext(Dispatchers.IO) {
api.fetchUser(id)
}
}
}
Korutinen i ViewModel anropar getUser precis som en vanlig avstängningsfunktion — utan att explicit ange avsändaren. withContext döljer detaljerna för trådväxling.
När flera IO-operationer måste utföras efter varandra, kombinerar withContext dem i ett enda block. Detta är effektivare än att slå in varje operation i en separat withContext:
suspend fun loadUserProfile(id: String): Profile {
return withContext(Dispatchers.IO) {
val user = api.fetchUser(id)
val posts = api.fetchPosts(id)
Profile(user, posts)
}
}
Båda operationerna utförs på Dispatchers.IO och Profile-resultatet skapas och returneras utan onödiga kontextväxlingar. Om operationerna är oberoende är det bättre att använda async för parallell exekvering.
I vissa scenarier måste kod utföras som inte kan avbrytas — till exempel att spara tillstånd när en skärm stängs. Kombinationen withContext + NonCancellable löser denna uppgift:
withContext(Dispatchers.IO + NonCancellable) {
cache.saveState(state)
analytics.logEvent("state_saved")
}
Operatorn + kombinerar två kontextelement: IO-avsändaren och NonCancellable-flaggan. Blocket utförs även om den överordnade korutinen har avbrutits — detta är användbart för slutgiltiga operationer.
Den interna implementeringen av withContext bygger på mekanismen Continuation — den centrala abstraktionen för Kotlin-korutiner. Varje avstängningspunkt (suspend point) sparar exekveringstillståndet i ett Continuation-objekt och withContext är inget undantag.
Kotlin-kompilatorn översätter withContext till ett anrop av metoden withContext från kotlinx.coroutines, som internt skapar en ny instans av DispatchedContinuation. Detta objekt omsluter den ursprungliga Continuation och ersätter avsändaren i den. Om den nya avsändaren skiljer sig från den nuvarande, avbryts exekveringen, blocket skickas till lämplig trådpool och efter slutförande — återupptas med den ursprungliga kontexten.
När withContext anropas med samma avsändare som korutinen redan kör på, aktiverar Kotlin fast-path: blocket utförs synkront, utan att skapa DispatchedContinuation och utan att skickas till trådpoolen. Detta gör withContext praktiskt taget gratis vid upprepade anrop med samma kontext. Enligt JetBrains benchmarks (kotlinx.coroutines 1.8) utförs fast-path på mindre än 0,1 μs.
Varje anrop av withContext med en annan avsändare skapar en ny DispatchedContinuation och kräver trådväxling — detta tar 1 till 5 μs beroende på belastning. För de flesta applikationer är denna fördröjning omärkbar, men i loopar med tusentals iterationer är det bättre att aggregera operationerna i ett enda withContext-block.
Även erfarna utvecklare gör misstag när de arbetar med withContext. Låt oss titta på fyra vanliga problem och sätt att förebygga dem.
Utvecklare slår ofta in varje rad i en separat withContext, istället för att kombinera operationerna i ett block. Varje extra anrop med en annan avsändare skapar overhead.
Rätt: kombinera sekventiella IO-operationer i ett withContext(Dispatchers.IO) { ... }. Om en del av operationerna är CPU-intensiva — använd withContext(Dispatchers.Default) inom samma block.
withContext utför kod sekventiellt. Om två oberoende nätverksförfrågningar slås in i en withContext kommer de att utföras en efter en. För parallellitet, använd async + await.
// Sekventiellt — långsamt
withContext(Dispatchers.IO) {
val a = api.fetchA()
val b = api.fetchB()
}
// Parallellt — snabbt
coroutineScope {
val a = async { api.fetchA() }
val b = async { api.fetchB() }
println("${a.await()} ${b.await()}")
}
Om korutinen avbryts under withContext, avbryts även blocket på Dispatchers.IO. För operationer som måste slutföras till varje pris (databasskrivning, skicka analys), kombinera withContext med NonCancellable.
Uppdatera aldrig View-komponenter inuti withContext(Dispatchers.IO). withContext återgår inte till Main förrän hela blocket är klart. Utför UI-uppdateringen efter withContexts avslutande klammerparentes — då kommer korutinen redan att vara på huvudtråden.
Vanliga frågor
withContext är en avstängningsfunktion som inte blockerar tråden, utan växlar kontext inuti en befintlig korutin. runBlocking är en bro mellan korutiner och vanlig kod som blockerar den aktuella tråden tills den är klar. withContext är säker för UI-tråden, runBlocking är det inte.
Nej, withContext är en avstängningsfunktion, så den kan endast anropas från en annan avstängningsfunktion eller från en korutin (launch/async). Från en vanlig funktion kan withContext inte anropas — för detta behövs runBlocking eller CoroutineScope.
Kotlin aktiverar fast-path — blocket utförs synkront på samma tråd utan växling. Overheaden är mindre än 0,1 μs. Detta är inte ett fel, men ett sådant anrop är överflödigt — utför koden helt enkelt utan withContext.
Undantag inuti withContext sprids på samma sätt som i vanlig kod — via try-catch. Om blocket kastar ett undantag sprids det till den överordnade korutinen och avbryter den om det inte hanteras. Använd try-catch inuti withContext eller runt det.
Nej, withContext skapar inte en ny korutin. Den använder den befintliga korutinen men ändrar tillfälligt dess kontext. Detta skiljer den från launch och async, som skapar underordnade korutiner. Beteendet bekräftas av källkoden för kotlinx.coroutines.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också