🦁

Flowで受け取ったレスポンスを共有したい

2021/01/25に公開

マスタデータなど画面表示をする際に使用して、後からもう一度参照をしたい時などに

private val request = repository.getRepo("sobaya-0141")

init {
    // 画面表示
    request.onEach {
        it.body()?.forEach {
            print(it)
        }
    }.launchIn(viewModelScope)
}

// ボタン押下時
fun getRepos() {
    viewModelScope.launch {
        request.firstOrNull {
            it?.body()?.forEach {
                print(it.name)
            }
            true
        }
    }
}

こんな感じに作ってしまうと(当たり前ですが)通信が2回走り無駄です。
特に大きめのデータ取得をする時には大きめの無駄になってしまいます。
こんな時の解決方法を紹介します。

ShareIn編

ViewModel

private val request = repository.getRepo("sobaya-0141")
private val res = request.shareIn(viewModelScope, SharingStarted.Eagerly, 0)

init {
    res.onEach {
        it.body()?.forEach {
            print(it)
        }
    }.launchIn(viewModelScope)
}

fun getRepos() {
    viewModelScope.launch {
        res.firstOrNull {
            it?.body()?.forEach {
                print(it.name)
            }
            true
        }
    }
}

最初に記載した時のサンプルに対してshareInを使ってresと言う変数が生えました。
また、initgetReposメソッド共にrequestを参照せずにresを参照しています。

これで起動時にinitで取得した情報がgetReposメソッドでも取得できるようになります。
要はFlowに対してShareInをするとSharedFlowが受け取れます。

ShareInの引数について

SharingStartedにはEagerlyとLazilyが指定できます。
Eagerlyはすぐに共有を開始、Lazilyは誰かが購読をした時に共有を開始と言う違いがあります。
0を指定しているreplayですが、SharedFlowはまだ追えてないのでよく分かってません
replayは過去N回分の値を保持しておく指定になります。
1なら前回の値を記憶していてくれるので、emit後に購読開始した場合でも最新の値が取得可能になります。

StateIn編

private val request = repository.getRepo("sobaya-0141")
private val res = request.stateIn(viewModelScope, SharingStarted.Eagerly, null)

init {
    res.onEach {
        it?.body()?.forEach {
            print(it)
        }
    }.launchIn(viewModelScope)
}

fun getRepos() {
    res.onEach {
        it?.body()?.forEach {
            print(it)
        }
    }.launchIn(viewModelScope)
}

StateInの引数

ShareInと同様にSharingStartedにはEagerlyとLazilyが指定できます。
こちらは初期値も指定してあげなければいけない違いがあります。

最後に

SharedFlowをあまり理解できてないですが、今回のように自クラス内で値の共有をしたいのであれば、
StateFlow(StateIn)の方がいいのかなと思います。
もっと勉強してから加筆修正すると思います。

サンプルプロジェクト
https://github.com/sobaya-0141/AllFlow/tree/state_in

Discussion