🦁
Flowで受け取ったレスポンスを共有したい
マスタデータなど画面表示をする際に使用して、後からもう一度参照をしたい時などに
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と言う変数が生えました。
また、init
とgetRepos
メソッド共に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)の方がいいのかなと思います。
もっと勉強してから加筆修正すると思います。
サンプルプロジェクト
Discussion