withContext

Switching Threads Safely

withContext(dispatcher) { } runs a block on another dispatcher, suspends the caller until it finishes, and returns its result. It is the standard way to make a function main-safe: callers can call it from the UI thread without freezing the screen.

class UserRepository(private val dao: UserDao, private val io: CoroutineDispatcher = Dispatchers.IO) {
    // main-safe: may be called from Dispatchers.Main
    suspend fun loadUser(id: Long): User = withContext(io) {
        dao.findById(id)          // blocking database call runs on IO
    }
}

Example

Example

Read on IO, process on Default, then continue on the original thread.

import kotlinx.coroutines.*

fun where() = Thread.currentThread().name.substringBefore("-worker")

suspend fun readCsv(): List<String> = withContext(Dispatchers.IO) {
    println("Reading file on ${where()}")
    listOf("apple,40", "mango,120", "banana,30", "grapes,90")
}

suspend fun totalPrice(lines: List<String>): Int = withContext(Dispatchers.Default) {
    println("Calculating on ${where()}")
    lines.sumOf { it.substringAfter(",").toInt() }
}

fun main() = runBlocking {
    println("Start on ${where()}")
    val lines = readCsv()
    val total = totalPrice(lines)
    println("Back on ${where()}: total = $total")
}

Output

Start on main
Reading file on DefaultDispatcher
Calculating on DefaultDispatcher
Back on main: total = 280

Tips

  • Switch dispatchers inside the function that does the blocking work (repository, data source), not in the UI layer.
  • Inject the dispatcher as a constructor parameter (io: CoroutineDispatcher = Dispatchers.IO) so tests can replace it.
  • Libraries like Room, Retrofit and Ktor already switch threads internally: you can call their suspend functions from Main directly.