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?

【Koltin】Kotlin Flow における「エラーリトライ」と「リプレイ」完全解説

Posted at

はじめに

Kotlin の Flow は、非同期ストリームを安全に扱える強力な仕組みです。
しかし、通信エラー・一時的な失敗・画面再生成 などの場面では、
「再試行(Retry)」と「再発行(Replay)」を正しく設計しないと
UI が止まったり、二重リクエストが発生したりします。

この記事では、それぞれの動作原理と実践的な設計パターンを解説します。


エラーリトライ(Retry)

概要

Flow がエラーを発生したとき、自動的に再試行する仕組み
ネットワーク不安定や一時的なサーバエラーに有効です。


retry オペレーター

val result = flow {
    emit(api.fetchData())  // ネットワーク通信
}.retry(3) { e ->
    e is IOException // IOException の場合のみ再試行
}.catch { e ->
    emit("Fallback: ${e.message}")
}

最大 3 回まで再試行。
条件関数の戻り値が true なら再試行されます。


動作イメージ


retryWhen オペレーター

条件をより細かく制御できます。

flow {
    emit(api.fetchData())
}.retryWhen { cause, attempt ->
    if (cause is IOException && attempt < 3) {
        delay(1000L * attempt) // 指数的バックオフ
        true
    } else {
        false
    }
}.catch {
    emit("Failed after retries")
}

例:通信失敗時に 1秒 → 2秒 → 3秒 の間隔で再試行。


Tips

状況 対応策
API 一時エラー retryWhen で指数バックオフ
例外をログ出力したい catch 内で Log.e()
UI に再試行ボタンを出したい ViewModel 側で emit(RetryState.Failed) を通知

リプレイ(Replay)

概要

過去に emit された値を再送(再発行)する仕組み。
主に SharedFlowStateFlow で使われます。


StateFlow のリプレイ

StateFlow は常に 最新の値を1つ保持 しています。
新しいコレクタが現れると、その「最新値」が即座に再送されます。

val counter = MutableStateFlow(0)

fun main() = runBlocking {
    launch {
        repeat(3) {
            delay(100)
            counter.value = it
        }
    }

    delay(250)
    counter.collect { println("Collector: $it") }
}

出力:

Collector: 2

collect した瞬間に「最新値」が再発行されている。
これが 1件リプレイ の仕組みです。


SharedFlow のリプレイ

MutableSharedFlow では、任意の件数のリプレイが可能です。

val events = MutableSharedFlow<String>(replay = 2)

fun main() = runBlocking {
    launch {
        events.emit("A")
        events.emit("B")
        events.emit("C")
    }

    delay(100)
    events.collect { println("Collector: $it") }
}

出力:

Collector: B
Collector: C

過去2件(B, C)が新規購読者に再送される。


replayCache で過去値を参照

println(events.replayCache) // 現在のリプレイ値リスト

Flow を Hot 化してリプレイ可能にする

shareIn() を使えば、Cold Flow を ホット + リプレイ可能 に変換できます。

val hotFlow = coldFlow.shareIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(),
    replay = 1
)

replay = 1 により、最新値を1件保持。
画面回転や再購読時にも値をすぐに受け取れる。


Retry × Replay 組み合わせ設計例

ViewModel 層での実戦構成

val uiState = repository.loadUser()
    .retryWhen { e, attempt ->
        e is IOException && attempt < 3
    }
    .catch { emit(UserState.Error(it.message)) }
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(),
        initialValue = UserState.Loading
    )

特徴:

  • Cold Flow(データ取得)を
    stateIn() で Hot 化し、最新状態をリプレイ。
  • 通信失敗は retryWhen で最大3回まで自動再試行。

Retry / Replay の比較表

機能 対象 タイミング 主な用途
Retry Flow のエラー エラー発生時 ネットワーク再試行
Replay Flow のデータ collect 時 最新値の再発行
stateIn() Cold → Hot ViewModel 内 状態保持と再発行
shareIn() Cold → Hot 複数UIで共有 イベント再発行
retryWhen() Flow 条件付きリトライ バックオフ付き再試行

実戦Tips

シナリオ 推奨設計
API失敗を自動再試行したい retryWhen
画面再生成時に前回状態を再表示 stateIn(replay=1)
複数画面で最新イベント共有 SharedFlow(replay=1)
UI側の手動リトライボタン ViewModel に retryTrigger Flow を作る

まとめ

概念 内容
エラーリトライ 一時的な失敗を再試行し、安定したデータ取得を実現
リプレイ 最新または過去の値を再発行して、UI復元を簡単に
組み合わせると 失敗に強く、再購読にも対応した堅牢な Flow 設計ができる

Retry = 「再試行」
Replay = 「再発行」

Kotlin Flow の堅牢設計は、この2つの正しい理解から始まる。
Clean Architecture と組み合わせれば、安定 × 再現性の高い UI を実現できる。

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?