https://www.youtube.com/watch?v=sk3svS_fzZM&list=PLQkwcJG4YTCQHCppNAQmLsj_jW38rU9sC&index=52
1. flow 的创建
在viewmodel中创建一个计时倒数的flow
val countDownFlow = flow<Int> {
val startValue = 10
var curValue = startValue
while (curValue > 0) {
delay(1000L)
curValue--
emit(curValue)
}
}
在compose中使用这个flow
val time by flowDemoViewModel.countDownFlow.collectAsState(10)
Text(text = time.toString())
可以看到,页面启动后,text的数字有10变到0
抛开界面,单纯的收集流
fun collectFlow() {
viewModelScope.launch {
countDownFlow.collect {time->
println("The current time is $time")
}
}
}
启动的时候开始收集
LaunchedEffect(null) {
flowDemoViewModel.collectFlow()
}
流输出
2. collect和 collectLatest 有什么区别呢?
我们在发送和接收的地方都添加上打印
//创建示例
val countDownFlow = flow<Int> {
val startValue = 10
var curValue = startValue
while (curValue > 0) {
delay(1000L)
curValue--
println("The current emit $curValue")
emit(curValue)
}
}
fun collectFlow() {
println("The current time collect")
viewModelScope.launch {
countDownFlow.collectLatest {time->
println("The current time is start $time")
delay(1500)
println("The current time is end $time")
}
}
}
先用collect,看下打印效果
countDownFlow.collect {}
数据发送端是一秒发送一个,但是接收地方需要1.5秒处理,但是使用collect的话,整个数据还是按顺序下来的
改成countDownFlow.collectLatest {}
collectLatest不会等待下游把事件处理完,而是继续发送,当下游收到新的事件后,会中止前面一个事件的处理,所以,我们只有看到,最后一个事件被处理完成,其他的都是知识开始,就中止了
对这个countdownflow的数据收集,数据流是独立,互不受影响的,例如我们对同一个流进行收集,一个用
val time = flowDemoViewModel.countDownFlow.collectAsStateWithLifecycle(10)
的方式,
一个用 countDownFlow.collectLatest {}
数据发送端其实是两个互不影响的流在发送,可以看到emit发送了两次
3.flow简单操作符filter,map,onEach
fun operatorFlow(){
viewModelScope.launch {
countDownFlow.filter {
it%2==0
}.onEach {
println("The current onEach $it")
}.map {
it*it
}.collect{
println("The current is $it")
}
}
}
filter 过滤,只放行符合条件的数据
onEach对每个数据进行一些读的操作,继续发送值
map 对数据进行一些转换后,发送转换后的数据
4.flow terminal操作符 count,reduce,fold
count对流中的数据个数进行统计
fun countFlow() {
viewModelScope.launch {
val count = countDownFlow.filter {
it % 2 == 0
}.onEach {
println("The current terminalFlow onEach $it")
}.count()
println("The current count terminalFlow is $count")
}
}
reduce
reduce 发送了5,4,3,2,1,过滤后只有5,3,1 然后累计成
5*3=15
15*1=15
最后reduce=15
fun reduceFlow() {
viewModelScope.launch {
val reduce = countDownFlow.filter {
it % 2 == 1
}.onEach {
println("The current terminalFlow onEach $it")
}.reduce { accumulator, value ->
//accumulator前面累计的值
//value新的数据
// accumulator+value //累计加
accumulator * value //累计乘
}
println("The current reduce terminalFlow is $reduce")
}
fold
在reduce的基础上,在buff叠加初始值,结果是1500
fun foldFlow() {
viewModelScope.launch {
val fold = countDownFlow.filter {
it % 2 == 1
}.onEach {
println("The current terminalFlow onEach $it")
}.fold(100) { accumulator, value ->
//在 reduce的基础上,再叠上初始值
accumulator * value //累计乘
}
println("The current fold terminalFlow is $fold")
}
5.flow的 flat操作
fun flatFlow() {
val flow1 = flow {
emit(1)
delay(1000)
emit(5)
}
viewModelScope.launch {
flow1.flatMapConcat { value ->
flow {
emit(value + 1)
delay(1500)
emit(value + 2)
}
}.collect { value ->
println("The current value$value")
}
}
}
第一个值是1,然后转换成 2 发送出去,过了一秒转换成3发送出去
第二个值是5,转换成6发送出去,再转换成7发送出去
flatMapConcat就是将流中的数据转换成另外一个流,然后这些流按顺序拼接
拼接之后就是 2,3,6,7
主要特点:
-
串联流:
flatMapConcat
会等到上一个流中的所有元素都被发射后,才会继续处理下一个流。这意味着,它会严格地按顺序将流中的所有元素一个接一个地进行处理和发射。 -
顺序性:
flatMapConcat
会确保处理顺序,只有当前的流全部处理完毕后,才会继续处理下一个流,这点和flatMapMerge
之类的操作符不同。
如果改成
viewModelScope.launch {
flow1.flatMapMerge { value ->
flow {
emit(value + 1)
delay(1500)
emit(value + 2)
}
}.collect { value ->
println("The current value$value")
}
}
发送端不会等下游处理完,而是直接发送下一个数据了
发送1,处理后变成2发送出去,这时候下游还没处理完
上游又发送了5,处理后变成6发送出去了,前一个的1变成3刚完成,发送了3
最后5转换成7,再发送。
如果是 flatMapLatest呢上一个没处理完的直接取消
所以我们丢失了3的结果。
6.flow buffer,conflate
按顺序执行的
fun flowDemo(){
val flow1 = flow {
emit("Appetize")
delay(1000)
emit("main dish")
delay(500)
emit("desert")
}
viewModelScope.launch {
flow1.onEach {
println("Flow:$it is delivered")
}
.collect{
println("Flow: start eating $it")
delay(1500)
println("Flow: finished eating $it")
}
}
}
普通的flow没有一个缓冲区,所以只能等前面的数据处理完之后,再发送下一个,但是如果我们给他加一个缓冲区buffer,这样,上游数据的发送不受下游处理速度的影响
buffer
这时候,上游发送的就比较快一些了
conflate
我们把吃的速度再放慢一些
fun flowDemo(){
val flow1 = flow {
emit("Appetize")
delay(1000)
emit("main dish")
delay(500)
emit("desert")
}
viewModelScope.launch {
flow1.onEach {
println("Flow:$it is delivered")
}.conflate()
.collect{
println("Flow: start eating $it")
delay(2000)
println("Flow: finished eating $it")
}
}
}
看下
因为吃的太慢了,主菜到的时候,开胃菜还没吃完,
然后甜品已经到了,开胃菜才吃完,这时候,我们来不及吃主菜
直接从甜品开始吃了,这个就是
conflate
conflate不会等下游,会一直发送数据
下游处理完数据之后,也只能继续处理当前最新的数据,而不管中间是否有遗漏未处理的数据
7.flow combine,zip
combine
两个数据中的任一一个数据变化,都会触发combine
private val flow1 = (1..10).asFlow().onEach {
delay(1000)
println("data:flow1 emit $it")
}
private val flow2 = (10..20).asFlow().onEach {
delay(300)
println("data:flow2 emit $it")
}
private val _numberString = MutableStateFlow<String>("init")
val numberString = _numberString.asStateFlow()
fun combineData() {
flow1.combine(flow2) { data1, data2 ->
println("data:combine $data1,$data2")
_numberString.value += "($data1,$data2)\n"
}.launchIn(viewModelScope)
}
由于flow2数据发送的比较快,10,11的数据没有匹配的flow1,也直接丢失了,直到有12之后才有匹配的
zip
两边数据都变化后,才触发zip
//需要两个数据 both
fun zipData() {
flow1.zip(flow2) { data1, data2 ->
println("data:zip $data1,$data2")
_numberString.value += "($data1,$data2)\n"
}.launchIn(viewModelScope)
}
merge
merge的话,就是把两个flow合并