hizi memo

びぼうろく📝

CoroutineExceptionHandler を用いて Coroutines のエラー処理を行う

この記事は Kotlin Advent Calendar 2018 の9日目の記事です. Kotlin の Coroutines を使う際、エラーハンドリングをいい感じにできないか調べてたのでメモ.

try-catch によるエラーハンドリング

Coroutines におけるエラーハンドリングといえば, 次のようなtry-catchを使用したサンプルが多くみられた.

GlobalScope.launch {
    try {
        // do something
    } catch (e: Exception) {
        // do something
    }
}

このやり方でももちろん正しく動作するが, Coroutinesを多用し, なおかつ, それぞれエラーハンドリングを適切に行う必要がある場面において try-catch が全体における可読性を下げてしまい, また, 毎回 try-catch を書くのが手間になってくると感じた.

CoroutineExceptionHandler によるエラーハンドリング

このようなケースにおいて, CoroutineExceptionHandlerは有効な解決策であると考えた. try-catch を書く代わりに, GlobalScope.launchの引数coroutineContextに渡してやるだけで, エラーハンドリングを丸投げすることができる. あとは, この CoroutineExceptionHandlerを使い回すだけで良い.

val errorHandler = CoroutineExceptionHandler { _, exception ->
    // do something...
    // ex. Timber.e(exception)
}

GlobalScope.launch(errorHandler) {
    throw RuntimeException() // crashすることなく, errorHandler に処理が移行する
}

try-catch を書く必要がなくなり, 非常にスッキリした. 実際, どのような状況で使用したかというと, RxJava を用いた Flux Architecture において, Action が持つ関数内で, Repositoryが返すエラーをDispatcherに伝播する部分で使用した. エラーがおおよそ3パターンくらい(IO, HTTP, その他)になるので, 共通の CoroutineExceptionHandlerで問題なかった. もし, エラーハンドリングがより複雑であれば共通で使用すると複雑化する可能性があるので注意.

雑感

try-catch 使うより CoroutineExceptionHandler のほうが良いなと思って書いた. RxJava脳なので, RxJavaのように 正常系と異常系が分離されているのが綺麗だと思い込んでいる. これが一般的に良いかどうか判断つかないが, 個人的にはtry-catchを毎回書く手間が気になっていたので解消されてよかった.

参考