0Pricing
Kotlin Academy · レッスン

SharedFlow:イベントバスとワンショットイベント

イベントブロードキャストのためにSharedFlowのreplayとextraBufferCapacityを設定します。

「SharedFlow:イベントバスとワンショットイベント」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

SharedFlow とは

SharedFlow は、アクティブなすべてのコレクターにブロードキャストするホットな Flow です。StateFlow とは異なり、現在の値という概念はなく、純粋にイベント駆動です。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val events = MutableSharedFlow<String>()
    launch { events.collect { println("Collector 1: $it") } }
    launch { events.collect { println("Collector 2: $it") } }
    delay(50)
    events.emit("UserLoggedIn")
    events.emit("DataRefreshed")
    delay(50)
    coroutineContext.cancelChildren()
}

replay パラメーター

replay は最後の N 件の発行をバッファーします。新しいコレクターは、過去のイベントを最大 N 件すぐに受け取ります。デフォルトは 0(リプレイなし)です。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = MutableSharedFlow<Int>(replay = 2)
    flow.emit(1); flow.emit(2); flow.emit(3)
    // Late subscriber gets last 2:
    flow.collect { print("$it ") }  // prints 2 3
}

extraBufferCapacity

extraBufferCapacity は、replay とは別にバッファー領域を追加します。送信側は、バッファーが満杯になるまでサスペンドせずに発行できます。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = MutableSharedFlow<Int>(
        replay = 0,
        extraBufferCapacity = 10
    )
    // Can emit up to 10 times without a collector ready:
    repeat(10) { flow.tryEmit(it) }
    launch { flow.collect { print("$it ") } }
    delay(50)
    coroutineContext.cancelChildren()
}

サスペンドしない発行には tryEmit

tryEmit(value) はサスペンドせずに発行し、バッファーが満杯の場合は false を返します。非サスペンドコンテキスト(コールバックやクリックハンドラー)で使用します。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = MutableSharedFlow<String>(extraBufferCapacity = 5)
    val ok = flow.tryEmit("click")  // non-suspending
    println("Emitted: $ok")
    launch { flow.collect { println(it) } }
    delay(50)
    coroutineContext.cancelChildren()
}

ワンショットイベント(UI ナビゲーション)

ナビゲーションや Snackbar の表示など、ワンショットの UI イベントには replay=0 の SharedFlow を使用します。再コンポジション時にイベントがリプレイされることはありません。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
class NavViewModel {
    private val _events = MutableSharedFlow<String>()
    val events = _events.asSharedFlow()
    fun navigateTo(route: String) {
        // viewModelScope.launch:
        kotlinx.coroutines.GlobalScope.launch { _events.emit(route) }
    }
}
// Collect in UI:
// viewModel.events.collect { route -> navController.navigate(route) }

SharedFlow と StateFlow の違い

StateFlow:現在の値を持つ状態(UI 状態)。SharedFlow:状態を保持しないイベント(ナビゲーション、Snackbar、分析)。利用者が最新の値を必要とするかどうかで選択します。

import kotlinx.coroutines.flow.*
// StateFlow: always has a value; new subscribers get current value
val uiState = MutableStateFlow("idle")

// SharedFlow: no stored value; events fire and are gone (unless replay>0)
val singleEvents = MutableSharedFlow<String>(replay = 0)

fun main() { println("State = what it is; Event = what happened") }

SharedFlow によるイベントバス

シングルトンの SharedFlow を使用して、アプリ全体のシンプルなイベントバスを実装します。RxJava の PublishSubject パターンに代わる方法です。

import kotlinx.coroutines.flow.*
object EventBus {
    private val _events = MutableSharedFlow<Any>(extraBufferCapacity = 100)
    val events = _events.asSharedFlow()
    fun post(event: Any) { _events.tryEmit(event) }
}
sealed class AppEvent {
    object UserLoggedOut : AppEvent()
    data class ShowError(val msg: String) : AppEvent()
}
// Usage: EventBus.post(AppEvent.UserLoggedOut)

subscriptionCount

sharedFlow.subscriptionCount は、アクティブなコレクター数を追跡する StateFlow です。上流のプロデューサーの開始や停止に役立ちます。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = MutableSharedFlow<Int>()
    println("Subscribers: ${flow.subscriptionCount.value}")  // 0
    val job = launch { flow.collect { } }
    delay(50)
    println("Subscribers: ${flow.subscriptionCount.value}")  // 1
    job.cancel()
    delay(50)
    println("Subscribers: ${flow.subscriptionCount.value}")  // 0
}

resetReplayCache

resetReplayCache() は、リプレイ用にバッファーされたキャッシュを消去します。リプレイされるイベントが古くなり、新しいサブスクライバーに表示したくない場合に役立ちます。

import kotlinx.coroutines.flow.*
fun main() {
    val flow = MutableSharedFlow<Int>(replay = 3)
    flow.tryEmit(1); flow.tryEmit(2); flow.tryEmit(3)
    println("Replay cache: ${flow.replayCache}")  // [1, 2, 3]
    flow.resetReplayCache()
    println("Replay cache: ${flow.replayCache}")  // []
}

SharedFlowのバックプレッシャー処理

コレクターの処理が遅い場合は、onBufferOverflowでSUSPEND(デフォルト)、DROP_OLDEST、DROP_LATESTのいずれかを選択します。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.channels.BufferOverflow
fun main() = runBlocking {
    val flow = MutableSharedFlow<Int>(
        replay = 0,
        extraBufferCapacity = 3,
        onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    repeat(10) { flow.tryEmit(it) }
    launch {
        flow.collect { println(it) }  // only sees most recent 3
    }
    delay(50)
    coroutineContext.cancelChildren()
}

タイムアウト付きの収集

タイムアウトを設定してSharedFlowを収集すると、有限個のイベントを処理した後に停止できます。テストや処理件数を制限した処理に便利です。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val events = MutableSharedFlow<String>()
    launch { repeat(5) { delay(50); events.emit("Event $it") } }
    withTimeoutOrNull(200) {
        events.collect { println(it) }
    }
    println("Done collecting")
}

確認問題

ナビゲーションのような1回限りのUIイベントには、どのreplay値を使用すべきでしょうか。

まとめ

SharedFlowはホットなイベントブロードキャスターです。1回限りのイベントにはreplay=0を使用し、後から購読するサブスクライバーにもイベントを届けるにはreplay>0を使用します。サスペンドできないコンテキストではtryEmitを使用します。イベントにはSharedFlow、状態にはStateFlowを選択します。

よくある質問

「SharedFlow:イベントバスとワンショットイベント」レッスンは無料ですか?

はい。「SharedFlow:イベントバスとワンショットイベント」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「SharedFlow:イベントバスとワンショットイベント」で何を学びますか?

イベントブロードキャストのためにSharedFlowのreplayとextraBufferCapacityを設定します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「SharedFlow:イベントバスとワンショットイベント」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. StateFlow:UI向けのHotな状態保持
  2. SharedFlow:イベントバスとワンショットイベント
  3. shareInとstateInでCold FlowをHotに変換する
  4. TurbineによるStateFlowとSharedFlowのテスト
← Kotlin Academyに戻る