Kotlin

Kotlin Coroutine の例外処理と Structured Concurrency 完全ガイド

ⓘ本ページはプロモーションが含まれています

お得なお知らせ

スポンサードリンク
AI時代のキャリア構築

プログラミング学習、今日から動き出す

「何から始めるか」で止まっている人こそ、無料説明会や本で自分に合うルートを30分で確定できます。

Enjoy Tech!|月額制でWeb系に強い▶ (Kindle本)ITエンジニアの転職学|後悔しないキャリア戦略▶

▶ AIコーディング環境なら  実践Claude Code入門(Amazon)が実務で即使える入門書です。Amazonベストセラーにも選ばれていますよ。


スポンサードリンク

1. Structured Concurrency と例外伝搬モデル

1-1. 基本概念

  • Structured Concurrency では、CoroutineScope がツリー構造になるように子コルーチンは必ず親スコープに紐付けられます。
  • Job が失敗すると、その例外は CancellationException にラップされて親 Job に伝搬し、親がキャンセルされるかどうかは使っている Job の種類に依存します。

1-2. 親子関係の実装例

  • SupervisorJob を用いた場合、子 A の例外は子 B に波及せず、親スコープ自体もキャンセルされません。
  • 逆に普通の Job()(デフォルト)を使うと、A が失敗した瞬間に 全体がキャンセル されます。

1-3. まとめ

Job の種類 子の例外が親へ伝搬するか 他の子への影響
Job() あり(全体がキャンセル) すべてキャンセル
SupervisorJob なし(例外はローカルに留まる) 失敗した子だけ停止

2. launchasync の例外捕捉パターン

2-1. launch → fire‑and‑forget 系

  • launch の戻り値は Job だけ。例外は内部で捕捉しない限り 自動的に親スコープへ伝搬 します。
  • 呼び出し側で try { viewModelScope.launch { … } } catch (…) と書いても捕まえられません(Qiita の解説と同様)。

2-2. async → 結果取得型

  • asyncDeferred<T> を返し、失敗は await() が呼ばれた瞬間に再スローされます。
  • したがって外側の try / catch が有効です。

2-3. まとめ

関数 例外捕捉場所 主な利用シーン
launch コルーチン内部 (try/catch) または CoroutineExceptionHandler UI 更新や fire‑and‑forget の副作用
async 呼び出し側 (await) 計算結果やネットワーク応答を取得したい場合

3. SupervisorJob と部分失敗の許容

3-1. 基本動作

  • SupervisorJob子の失敗をローカライズ し、他の子は継続させます。
  • この特性は「部分失敗だけをリトライ対象にしたい」シナリオで威力を発揮します。

3-2. viewModelScope に付与するときの注意点

viewModelScope は内部で 既に Job が管理 されています。
単純に viewModelScope + SupervisorJob() と書くと、次の問題が起こり得ます。

問題 内容
二重 Job の生成 元の Job と新たに作られた SupervisorJob が別個に存在し、キャンセル伝搬が期待通りにならないケースがあります。
例外ハンドリングの混乱 失敗した子コルーチンはどちらの Job に紐付くかで挙動が変わるため、デバッグが困難になります。

正しい組み合わせ方

  1. SupervisorScope ビルダーを利用
    kotlin
    viewModelScope.launch {
    supervisorScope { // ここだけが SupervisorJob を持つ
    launch { fetchUser() }
    launch { fetchPosts() }
    }
    }
  2. カスタムスコープを作成する場合は viewModelScope.coroutineContext に上書き
    kotlin
    private val vmScope = CoroutineScope(
    viewModelScope.coroutineContext + SupervisorJob()
    )
    // 以降 vmScope.launch { … } と使用すれば、元の Job が置き換わります。

3-3. まとめ

  • SupervisorJob は部分失敗を許容 したいときにだけ導入し、既存スコープとの重複 に注意する。
  • Android の viewModelScopelifecycleScope に追加したい場合は、supervisorScope {} または viewModelScope.coroutineContext + SupervisorJob() を用いるのが安全です。

4. CoroutineExceptionHandler のベストプラクティス

4-1. ハンドラの登録と適用範囲

  • CoroutineExceptionHandler未捕捉例外 のみを受け取ります。
  • async 系で await() を呼び出すと例外はローカルに再スローされるため、ハンドラには届きません。

4-2. SupervisorJob + Handler の実装例

  • 部分失敗は SupervisorJob がローカライズ、未捕捉例外はハンドラが一元管理します。
  • この構成は Android だけでなくサーバーサイド(Ktor、Spring)でも同様に有効です。

4-3. まとめ

要素 目的
SupervisorJob 子の失敗が兄弟や親へ波及しないようにする
CoroutineExceptionHandler 未捕捉例外を一元的にロギング・解析ツールへ送信

5. Android コンポーネントでの実装とテスト

5-1. ViewModel と Lifecycle のカスタムスコープ

  • viewModelScope元々の Job を上書きする形で SupervisorJob() を付与しているため、二重キャンセルは起きません。
  • 同様に Fragment では viewLifecycleOwner.lifecycleScope + SupervisorJob() + handler と組み合わせます。

5-2. コルーチン例外のユニットテスト(kotlinx‑coroutines‑test)

  • StandardTestDispatcher により 即時実行 が保証され、例外の伝搬経路を deterministic にテストできます。
  • SupervisorJob の有無で fetchPosts() が呼び出されるかどうかが判定ポイントになるため、部分失敗の正しさを検証可能です。

5-3. リファクタリングチェックリスト

項目 確認手順
スコープの統一 GlobalScope が残っていないか。すべて viewModelScope / lifecycleScope またはカスタムスコープに置き換える。
SupervisorJob の適切な導入 同一スコープ内で独立タスクが 2 個以上ある場合、supervisorScope {}SupervisorJob() を付与しているか。
ExceptionHandler の設定漏れ ViewModel・Repository 等、例外が流出しうる層にハンドラが設定されているか。
二重 Job の回避 viewModelScope + SupervisorJob() と書くときは viewModelScope.coroutineContext に上書きしていることを確認。
テストカバレッジ ネットワーク・DB 例外シナリオがすべて runTest で網羅されているか。
ログ/Analytics の一元化 CoroutineExceptionHandler がプロジェクトのロギング基準に沿った出力を行っているか。
Lifecycle 連携 スコープ破棄時に子コルーチンが確実にキャンセルされていることを LeakCanary 等で検証。

5-4. まとめ

  • viewModelScopeSupervisorJob の組み合わせは、元の Job を置き換える形で 行うと安全です。
  • ハンドラ・テスト・チェックリストを併用すれば、例外が原因でアプリ全体がクラッシュするリスクを 実務レベルで低減 できます。

6. 最終まとめ

観点 推奨パターン
例外伝搬の制御 子失敗だけ局所化したい → SupervisorJob(または supervisorScope {}
結果取得が必要な非同期処理 async + await で例外を呼び出し側で捕捉
fire‑and‑forget の UI 更新系 launch 内で try/catch、もしくは全体ハンドラに委譲
未捕捉例外の一元管理 アプリ全体のベーススコープに CoroutineExceptionHandler を設定
Android コンポーネントへの適用 viewModelScope.coroutineContext + SupervisorJob() + handler でカスタムスコープを作成
テスト戦略 kotlinx‑coroutines‑testrunTest とモック例外で部分失敗シナリオを検証

本稿の内容は、実装・レビュー・テストの全フェーズで活用できる 実践的なチェックポイント を網羅しています。ぜひプロジェクトに取り入れ、安全かつ保守性の高いコルーチンコードを書き上げてください。

スポンサードリンク

お得なお知らせ

スポンサードリンク
AI時代のキャリア構築

プログラミング学習、今日から動き出す

「何から始めるか」で止まっている人こそ、無料説明会や本で自分に合うルートを30分で確定できます。

Enjoy Tech!|月額制でWeb系に強い▶ (Kindle本)ITエンジニアの転職学|後悔しないキャリア戦略▶

▶ AIコーディング環境なら  実践Claude Code入門(Amazon)が実務で即使える入門書です。Amazonベストセラーにも選ばれていますよ。


-Kotlin