エラーハンドリングの原則 - Ki-Kobayashi/Android-Wiki GitHub Wiki

🟩 「runCatching」と「try / finally」 の違い

🟡finally の違い

  • try / finally では、例外がスローされたかどうかに関係なく、finally ブロックが常に実行されることを保証する
fun tryToDoSomething(): Int {
    var result = 0
    val resource = acquireResource()
    try {
        // Code that can throw an exception
        result = performAction(resource)
    } finally {
        releaseResource(resource)
    }
    return result
}
  • runCatching()ブロックの後に手動でリソースを解放する
fun doSomethingRunCatching(): Int {
    val resource = acquireResource()
    val result = runCatching {
        // Code that can throw an exception
        performAction(resource)
    }
    releaseResource(resource)
    return result.getOrThrow()
}

※コードで例外を処理せずにリソースのクリーンアップが必要な場合は、try / finally

🟡 キャッチするスコープの違い

  • try-catch: catchブロックに書いたものだけを catch
  • runCatching: すべてを catch (値を返すか、例外をスロー)

.# 🟩 不必要に広い範囲の例外をキャッチすることは、バグの早期発見の妨げ

.

🟩 API接続に関するエラーハンドリングの原則

https://engineering.linecorp.com/ja/blog/droidkaigi2023_code_review_challenge_4

  • ネットワーク関連のトラブルを示す IOException のみをハンドリングすれば十分

.

🛑【runCathing】

runCathingを使用することでその他の例外も全てキャッチする

例えば、 NullPointerException は"実装の問題"から生じる。そのためコードを修正する必要があるが、 runCatching を使っていると発見が難しくなってしまう。
CancellationException は Coroutine が正しく動作するために必要な例外。こちらをキャッチしてしまうと、正常にキャンセルが動作しなくなる

  →そのため必要最小限の例外をキャッチするよう心がけるべき

    suspend fun getUserData(userId: UserId): UserData? = try {
        // ...snip
    } catch (e: IOException) {
        null
    }

    suspend fun getUserData(userName: UserName): UserData? = try {
        // ...snip
    } catch (e: IOException) {
        null
    }

🟩 コルーチンでtry-catchする罠

https://note.com/actbe_ohashi/n/n69a6d72db2c7

Coroutine内 の 【async に対して】、エラーハンドリングしたいなら、try-catch はあまり使わないほうがいい?
  → CoroutineExceptionHandler だったりを使った方が良さそう

🟡 事例(理由)

Coroutine内 の async の内部がキャッチされない

lifecycleScope.launch {
    try {
        async {
            // 例外に落ちるかもしれない処理
        }
    } catch (exception: Exception) {
        Log.d(TAG, "落ちたのをログで確認したい${exception.message}")
    }
}

🟡 解決案(try-catchをそのまま使いたい場合)

👇の回答もあるが、そもそも「lifecycleScope.launch」自体をtry-catchで囲うとどうなる?

lifecycleScope.launch {
    try {
        coroutineScope {
            async {
                // 例外に落ちるかもしれないの処理
            }
        }
    } catch (exception: Exception) {
        Log.d(TAG, "落ちたのをログで確認したい${exception.message}")
    }
}

.

🟩 エラーハンドリングの方法3種

https://medium.com/@midoripig1009/functional-error-handling-in-kotlin-either-try-and-result-da9e5ea7a97f

  • try-catch
  • Either
  • Result

🟡try-catch

.## 🟡Either

.## 🟡Result

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.