どうもこんばんわ。
iPhone 15 の発表会があったそうですが普通に寝てました、
みんなUSB Type-C
やらミリ波アンテナ非搭載
ばっか目が行ってるけど、Pro
の方だとAV1のハードウェアデコーダー
が入ってるらしいじゃん!!
まぁAV1 コーデック
使う機会が来るのかはまた...
本題
flow
でcollect
した値を元に、別のflow
をcollect
したい。
適当に例を書いたけど、こんな感じにユーザーを返すFlow
の値を使って、下のコメントを返すFlow
を収集したい。
解決
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/map-latest.html
mapLatest
か collectLatest
を使えば解決
前回の収集をキャンセルして、常に最新の値で別のFlow
をcollect
できる
こんな感じに、getRealtimeUserIdList()
が新しい値を発行したら、既に動いているgetRealtimeUserComment()
の方はキャンセルされ、新しくgetRealtimeUserComment()
を作り購読するようにしています!これがやりたかったんだよな~
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/transform-latest.html
また、Android
のJetpack Compose
とかで使いたい場合は、Flow#collectAsState
したいと思うので、
その場合はcollectLatest
ではなく、変換したFlow
を返すtransformLatest
を選ばないとダメですね。
// collect するもよし
commentFlow.collect {
println(it)
}
// Jetpack Compose の State として使うのもよし
commentFlow.collectAsState()
ほかにも、List<User>
みたいに配列で欲しい(Flow
の返り値をFlow<Message>
ではなく、Flow<List<Message?>>
にしたい)場合はこんな感じですかね
うーーんあんまりきれいに書けなかった...
ちなみに
Flow<List<User>>
ではなく、Flow<User>
みたいに、配列ではない場合はもっと簡単にかけます。
まぁ複数あったとしてもFlow#merge
で1つのFlow
にしてしまえばおっけーだと思います。
ちなみ2
println("receive new user")
やprintln("receive new user list")
は分かりやすくしているだけなので、なくても動く(当たり前)
もっと詳しく話せ
まず前提になるコードです
適当にデータクラスを用意して、それぞれ定期的に適当にFlow
を用意しました、
ユーザーを購読して~コメントを購読するとするとまずこう
しかし、これだと、Flow
の更新のたびにgetRealtimeUserComment()
を呼び出しています。
これが期待値の場合もありますが、今回は値を受け取ったら既に動いているflow
をキャンセルしてほしい
と思った時に書くやつ
別のコルーチンスコープを作って、ユーザー用とコメント用で分けて、コメント用は更新のたびにキャンセルする。
たしかにこれでも動くのですが、、、もっとなんかいい方法があるはず!
というわけでcollectLatest
/mapLatest
/transformLatest
の登場。
前回のブロックをキャンセルしてくれます。以上です。
つまり...
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/map-latest.html
collect { }
の中でコルーチン使いたい時(collect { }
したい等)ときは、collectLatest { }
を親にする?と次の値が来た時にキャンセルして再起動してくれる。
コードおいていきます
https://github.com/takusan23/FlowLatest
おわりに
Latest
系のオペレーターはなぜか@OptIn
しないとLint
で怒られます...
早く安定になって欲しい