🍣

BroadcastChannelとSharedFlow

2021/01/29に公開

Flowを使った事がないけど興味はある人向けの第二弾です。
飛び道具的な使い方なので制限はしたいですが、個人的にはちょこちょこ使う書き方を紹介します。

実現したいこと


おいしい健康Android版アプリ

おいしい健康Androidアプリの検索画面です。
この画面で1の体調をタップすると、体調タブ(2)にカウントが付き、画面下部の検索条件(3)やヒット数(4)が変化します。
このように1イベントで複数の動作をする(受け取ったデータをみんなに共有する)動作を作ろうと思います。
※せっかくなのでダウンロードしてね。
※実際にこの部分はBroadcastChannel版は使ってなかったはずです(いいサンプルが無いので使いました)

BroadcastChannel版

SharedFlowなんて存在してなかった時代の老人なので、こちらを使ってる事が多いです。

Dispatcher

class Dispatcher {
    private val testProcessor = BroadcastChannel<String>(Channel.BUFFERED)
    val testFlow = testProcessor.asFlow()
    suspend fun testSend(args: String) = testProcessor.send(args)
}

repositoryにおいてる人が多そうですが、個人的にはrepositoryじゃない気がするのと、
FluxアーキテクチャのDispatcherに似てる感じがするので、Dispatcherと名付けてます。

このクラスを必要な箇所でinjectして使います。

イベントを発行する部分

viewLifecycleOwner.lifecycleScope.launchWhenStarted {
    dispatcher.testSend("何かのイベント1")
    viewModel.recieve()
}

Dispatcherのsendメソッドを呼ぶだけです。

イベントを受け取る部分

dispatcher.testFlow.onEach {
    println("COLLECT receive $it")
}.launchIn(viewModelScope)

こちらもDispatcherのasFlowしたBroadcastChannelを購読するだけです。

注意点

viewLifecycleOwner.lifecycleScope.launchWhenStarted {
    dispatcher.testSend("何かのイベント1")
    viewModel.recieve() // ここで購読開始
}
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
    dispatcher.testSend("何かのイベント2")
}

のようにすると

COLLECT receive 何かのイベント2

となり購読開始後のイベントしか受け取れません
なので、購読開始後にイベントを発行(send)となるように注意してください

SharedFlow版

class Dispatcher {
    private val _testFlow = MutableSharedFlow<String>(0)
    val testFlow: SharedFlow<String> = _testFlow
    suspend fun testSend(args: String) = _testFlow.emit(args)
}
  • BroadcastChannelを使っていたところをMutableSharedFlowに変更
  • testSendメソッドにてsendしていたのをemitに変更
    だけで同一の動作となります。

これだけだと違いがないじゃんで終わってしまいつまらないですが、SharedFlowには別の能力があります。

private val _testFlow = MutableSharedFlow<String>(1)

インスタンス生成時に引数に0を渡していた物を1にしてみます。

そして、BroadcastChannel版の注意で書いたコードを実行すると

2021-01-29 13:02:55.433 3724-3724/sobaya.example.allflow I/System.out: COLLECT receive 何かのイベント1
2021-01-29 13:02:55.435 3724-3724/sobaya.example.allflow I/System.out: COLLECT receive 何かのイベント2

購読前のイベントも受け取る事ができました。

これは引数で指定した回数分の値を覚えておき、購読開始した瞬間に伝えてくれます。

最後に

このように複数箇所で一つのイベント(そのデータ)が欲しい場合に、とても簡単にコードを書く事ができます。
別のFragmentにイベントを通知したりなど夢が広がります(Roomよりもお手軽ですし)

サンプルコード(channelとsharedflowで別コミットにしてあります。)
https://github.com/sobaya-0141/AllFlow/tree/channel_flow

Discussion