公開日: 2023/12/10
Shibuya.apk #45 にて以下の発表がありました。
https://speakerdeck.com/lycorptech_jp/dont-use-runcatching
本記事に必要な部分のみを要約すると下記の通りです。
優秀なAndroidエンジニアからCoroutineExceptionHandlerについても同じことが言えるのではないかという提案がありました。
CoroutineExceptionHandlerはコルーチン内でキャッチされなかった例外を処理することができ、実装は以下のようになっています。
public inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler =
object : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
override fun handleException(context: CoroutineContext, exception: Throwable) =
handler.invoke(context, exception)
}
handle
のラムダに実装者はエラー時の処理を記述することでエラーハンドリングを行うことができます。
// 例
private val exceptionHandler: CoroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
// エラー時の処理
}
..
viewModelScope.launch(exceptionHandler) {
// 非同期処理
}
ただこのhandle
はThrowable
を引数にとるので前述の通り、Errorも含まれてしまいます。
Errorの場合はアプリをただちに終了させることが望ましいので、このまま使用するのはよろしくありません。
前述の下記の通り実装をする必要があります。
private val exceptionHandler: CoroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
if (throwable !is Exception) throw throwable
// エラー時の処理
}
ただ毎回これを書くのは冗長ですし、Exception判定の実装漏れが発生する可能性があります。
そこでCoroutineExceptionHandlerの実装を踏襲して、下記のようなメソッドを作成すると良さそうです。
inline fun throwableCoroutineExceptionHandler(crossinline handler: (CoroutineContext, Exception) -> Unit): CoroutineExceptionHandler =
object : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
override fun handleException(context: CoroutineContext, exception: Throwable) {
if (exception !is Exception) throw exception
handler.invoke(context, exception)
}
}
使用方法は下記のようになります。
private val throwablExceptionHandler = throwableCoroutineExceptionHandler { _, exception ->
// エラー時の処理
}
..
viewModelScope.launch(exceptionHandler) {
// 非同期処理
}
以上です!
参考になれば幸いです!