0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlin の `withContext` 完全ガイド

Last updated at Posted at 2025-10-15

はじめに

〜スレッドを安全に切り替える suspend 関数〜


概要(Overview)

withContext は、コルーチンのコンテキスト(特にスレッド Dispatcher)を一時的に切り替えるための関数 です。
主に「重い処理をバックグラウンドに」「結果をUIスレッドに戻す」などの場面で使います。

一言で言うと

withContext = 一時的に別スレッドで処理し、結果を返す suspend 関数

たとえば:

val result = withContext(Dispatchers.IO) {
    // I/O専用スレッドで実行
    fetchDataFromNetwork()
}
// 結果をもとにUI更新(Mainスレッド側)

基本構文

suspend fun <T> withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T
  • 引数
    • context:実行するスレッド(例:Dispatchers.IO
    • block:実際に処理を行うsuspendラムダ
  • 戻り値
    • block の戻り値そのものを返す

コード例①:スレッドを切り替える

import kotlinx.coroutines.*

suspend fun fetchData(): String {
    return withContext(Dispatchers.IO) {
        println("Fetching on ${Thread.currentThread().name}")
        delay(1000)
        "Data loaded"
    }
}

fun main() = runBlocking {
    val data = fetchData()
    println("Received on ${Thread.currentThread().name}: $data")
}

出力例:

Fetching on DefaultDispatcher-worker-1
Received on main: Data loaded

IOスレッドで処理 → Mainスレッドで結果を受け取る


コード例②:UIスレッドとの連携(Android実戦)

class UserViewModel : ViewModel() {

    fun loadUser() {
        viewModelScope.launch(Dispatchers.Main) {
            val user = withContext(Dispatchers.IO) {
                getUserFromDatabase()
            }
            updateUI(user)
        }
    }
}

ポイント:

  • withContext(Dispatchers.IO):DBからデータ取得(I/O処理)
  • 戻り値が完了したら自動でDispatchers.Mainに戻る
  • UIスレッドをブロックせず安全に更新できる

コード例③:ネストしたスレッド切り替え

runBlocking {
    val result = withContext(Dispatchers.Default) {
        val ioResult = withContext(Dispatchers.IO) {
            "File content"
        }
        "Processed: $ioResult"
    }
    println(result)
}

Dispatcherの切り替えをネストしてもOK
コルーチンがスレッドを自由に「行き来」できます。


コード例④:例外処理とキャンセル

withContextsuspend関数なので、通常のtry-catchで例外を処理できます。

try {
    val result = withContext(Dispatchers.IO) {
        if (Random.nextBoolean()) throw IOException("Network error")
        "Success"
    }
    println(result)
} catch (e: IOException) {
    println("Caught: ${e.message}")
}

ポイント:

  • withContext 内の例外は呼び出し元に伝播
  • 親コルーチンのキャンセルも自動伝播(構造化並行性)

コード例⑤:計算とI/Oを組み合わせる

suspend fun analyzeFile(): Int {
    val text = withContext(Dispatchers.IO) {
        File("data.txt").readText()
    }
    return withContext(Dispatchers.Default) {
        text.split("\\s+".toRegex()).size
    }
}

I/OCPU計算 の両方を効率よく分離できる構造。

  1. I/O は Dispatchers.IO
  2. 計算は Dispatchers.Default

withContext vs launch vs async

関数 目的 戻り値 並列性 用途
launch 処理を「開始」する Job あり(独立) Fire-and-forget 処理
async 並列に「結果を返す」 Deferred<T> あり 並列計算
withContext スレッドを「切り替える」 T なし(順次) 一時的なコンテキスト変更

withContext は「別スレッドで実行して結果を受け取る」直列的構造。
並列実行したいときは async を使う。


内部的な動作イメージ

withContext一時的にIOへ「ジャンプ」し、処理が終わったら元のスレッドに戻る


性能最適化のポイント

  • withContext の呼び出しは軽量(数十ナノ秒〜数百ナノ秒程度)
  • スレッド切り替えコストはOS依存(不要な切り替えは避ける)
  • 小さいタスクを大量に切り替えるより、処理をまとめて1回切り替える方が効率的

注意点

注意点 説明
ブロッキングAPIをMainで使わない UIフリーズの原因になる
無駄な切り替えを避ける withContext(Dispatchers.IO) の入れ子多用は非効率
withContext は suspend関数内でのみ使用可能 通常関数から直接呼べない

まとめ

概念 説明
withContext コルーチンのスレッドを安全に切り替える
特徴 suspend関数・結果を返す・構造化並行性対応
よく使う組み合わせ Dispatchers.IO / Dispatchers.Default / Dispatchers.Main
利点 UIフリーズ防止、非同期I/Oの簡潔化、明確な責務分離
注意 無駄なスレッド切り替えは避けること

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?