Kotlin CoroutinesMiddleCoding
Что такое withContext() и когда его следует использовать?
withContext() переключает корутину на другой диспетчер без создания новой корутины, приостанавливается до завершения блока и возвращает результат; используется для переноса блокирующих или CPU-интенсивных операций на нужный пул потоков.
withContext() в Kotlin Coroutines
withContext(context) — suspend-функция, которая выполняет блок кода в указанном CoroutineContext, приостанавливает текущую корутину до завершения блока и возвращает его результат. В отличие от launch и async, она не создаёт новую корутину, а изменяет контекст существующей.
Базовый синтаксис
import kotlinx.coroutines.*
suspend fun readFile(path: String): String =
withContext(Dispatchers.IO) {
// Выполняется на IO-пуле, не блокирует main/default
java.io.File(path).readText()
}
suspend fun processData(data: String): Result =
withContext(Dispatchers.Default) {
// CPU-интенсивная работа на Default-пуле
heavyComputation(data)
}
Переключение диспетчеров
Типичный паттерн Android: ViewModel запускает корутину на Main, переключается на IO для данных, возвращается на Main для UI-обновления:
class UserViewModel(private val repo: UserRepository) : ViewModel() {
fun loadUser(id: String) {
viewModelScope.launch { // Main dispatcher
_uiState.value = UiState.Loading
val user = withContext(Dispatchers.IO) { // переключаемся
repo.fetchUser(id) // network/DB call
} // автоматически возвращаемся на Main
_uiState.value = UiState.Success(user) // снова на Main
}
}
}
withContext vs async/await
// withContext — последовательное выполнение, возвращает значение
val user = withContext(Dispatchers.IO) { repo.getUser(id) }
val orders = withContext(Dispatchers.IO) { repo.getOrders(id) }
// ^ Это последовательно! user загружается, потом orders
// async — параллельное выполнение
val userDeferred = async(Dispatchers.IO) { repo.getUser(id) }
val ordersDeferred = async(Dispatchers.IO) { repo.getOrders(id) }
val user = userDeferred.await()
val orders = ordersDeferred.await()
// ^ Параллельно! Быстрее, если задачи независимы
withContext(NonCancellable)
Специальный случай: cleanup-код, который должен выполниться даже при отмене корутины:
suspend fun saveAndClose(connection: Connection) {
try {
connection.save()
} finally {
withContext(NonCancellable) {
connection.close() // выполнится даже если корутина отменена
}
}
}
Диспетчеры и их назначение
Dispatchers.Main— UI-поток Android, обновление View/StateDispatchers.IO— сеть, файлы, база данных (блокирующий IO)Dispatchers.Default— CPU-интенсивные вычисления, парсингDispatchers.Unconfined— не переключает поток; только для специальных случаев
Подводные камни
- withContext последовательно: два withContext подряд — это два последовательных переключения, не параллельное выполнение.
- Частые переключения контекста (withContext в цикле) создают overhead; батчируйте работу.
- withContext(Dispatchers.IO) не защищает от бесконечного блока — всегда добавляйте withTimeout.
- Изменение UI-состояния внутри withContext(Dispatchers.IO) вызовет crash на Android (только Main).
- NonCancellable нельзя использовать за пределами finally — это архитектурная ошибка.
- withContext не создаёт новую корутину, поэтому CoroutineExceptionHandler родителя перехватит его исключения.
- Вложенные withContext(Dispatchers.IO) внутри друг друга — лишний overhead без пользы.
Common mistakes
- Объяснять «withContext()» только как синтаксис и не описывать поведение runtime/compiler.
- Игнорировать важный риск: Нельзя использовать withContext как универсальный способ спрятать blocking работу без выбора правильного dispatcher-а.
- Давать пример без edge case: отмены, null, recomposition, platform boundary или ошибки.
What the interviewer is testing
- Формулирует суть темы «withContext()» своими словами и связывает ее с кодом.
- Называет механизм: Он сохраняет structured concurrency: вызывающая coroutine suspend-ится, а дочерняя работа отменяется вместе с родителем.
- Видит production-последствие: Нельзя использовать withContext как универсальный способ спрятать blocking работу без выбора правильного dispatcher-а.