kotlin flow (二)

Flow操作符

  • buffer(int)

该操作符会新起一个协程来收集buffer之前的代码运行结果,新协程通过channel通知flow所在的协程,
并且与当前flow所在协成并行运行,如果缓冲区满了,会暂停产生新的数据等到收集器把缓冲区的数据消费完。
参数指定缓冲区的大小

flowOf("A", "B", "C")
    .onEach  { println("1$it") }
    .collect { println("2$it") }
输出
Q : -->-- [1A] -- [2A] -- [1B] -- [2B] -- [1C] -- [2C] -->--

flowOf("A", "B", "C")
    .onEach  { println("1$it") }
    .buffer()  // <--------------- buffer between onEach and collect
    .collect { println("2$it") }
输出
P : -->-- [1A] -- [1B] -- [1C] ---------->--  // flowOf(...).onEach { ... }

                      |
                      | channel               // buffer()
                      V

Q : -->---------- [2A] -- [2B] -- [2C] -->--  // collect

协序之间使用一个通道将协同程序P发出的元素发送到协同程序Q。如果缓冲操作符之前的代码(协同程序P中)比缓冲操作符之后的代码(协调程序Q中)快,则该通道将在某个时间点变满,并将暂停生产者协同程序P,直到消费者协同程序Q赶上。capacity参数定义此缓冲区的大小。

  • catch

捕获catch上游不包含取消异常的所有异常

fun errorTest(){
        viewModelScope.launch {
            flowOf(1,2)
                .map { repository.error() }
                .catch {
                    Log.d(TAG, "errorTest:catch $it")
                }
                .flowOn(Dispatchers.IO)
                .collect {
                    Log.d(TAG, "errorTest:collect $it")
                }
        }
    }

//error方法直接抛出异常

fun error():String{
        throw NullPointerException()
        //return "Test catch"
    }
//打印结果
FlowViewModel: errorTest:catch java.lang.NullPointerException
  • collect

终端操作符,触发flow运行并收集flow中发送的数据,是挂起函数只能在协程中调用

  • collectIndexed

带下标的收集

  • collectLatest

与 collect的区别是 ,有新值发出时,如果此时上个收集尚未完成,则会取消掉上个值的收集操作

  • combine

生成一个新的flow,改flow的值通过{}中的转换规则,组合每个流最近发出的值生成

val flow = flowOf(1, 2).onEach { delay(10) }
val flow2 = flowOf("a", "b", "c").onEach { delay(15) }
flow.combine(flow2) { i, s -> i.toString() + s }.collect {
    println(it) // Will print "1a 2a 2b 2c"
}
  • combineTransform

combine+Transform

  • conflate

buffer(CONFLATED)实现的,主要区别在于buffer会等待收集器而conflate抛弃中间的数据收集最新的数据,因为发送数据不会停止而收集数据又比较慢,所以当收集器收集数据的时候可能数据已经发送了很多条,收集器只收集最新的一条

fun conflateTest() {
        viewModelScope.launch {
            val flow = flow {
                for (i in 1..30) {
                    delay(100)
                    emit(i)
                }
            }
            flow.conflate()
                .onEach { delay(200) }
                .collect {
                    Log.d(TAG, "conflateTest:conflate $it")
                }
        }
    }
打印结果
conflateTest:conflate 1
conflateTest:conflate 2
conflateTest:conflate 4
conflateTest:conflate 6
conflateTest:conflate 8
conflateTest:conflate 10
conflateTest:conflate 12
conflateTest:conflate 14
conflateTest:conflate 16
conflateTest:conflate 18
conflateTest:conflate 20
conflateTest:conflate 22
conflateTest:conflate 24
conflateTest:conflate 26
conflateTest:conflate 28
conflateTest:conflate 30

发送的数据比收集快一倍刚好中间间隔一个数字

  • consumeAsFlow

将channel转换成flow,只能有一个数据收集者,如果添加多个数据收集器会抛出llegalStateException异常

mDemoChannel.consumeAsFlow()
  • receiveAsFlow

将channel转换成flow,允许有多个收集器,但不是多播,可能会轮流收到值

  • count()返回此流中的元素数

fun countFlow(){
        viewModelScope.launch {
            val count = flowOf(1,2,3,4).count()
            Log.d(TAG, "countFlow:count $count")
        }
    }
FlowViewModel: countFlow:count 4
  • debounce

返回一个新flow,改flow中的元素是通过给定时间内最新的一个值,其他的过滤掉

fun debounceTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                delay(90)
                emit(2)
                delay(90)
                emit(3)
                delay(1010)
                emit(4)
                delay(1010)
                emit(5)
            }.debounce(1000).collect {
                Log.d(TAG, "countFlow:count $it")
            }
        }
    }
输出:3,4,5
  • distinctUntilChanged

返回一个flow,过滤连续重复的值

fun distinctUntilChangedTest() {
        viewModelScope.launch {
            flowOf("hello","kotlin","kotlin","hello")
                .distinctUntilChanged()
                .collect {
                    Log.d(TAG, "distinctUntilChangedTest:distinctUntilChanged $it")
                }
        }
    }
输出:hello,kotlin,hello
  • distinctUntilChangedBy

返回一个flow,过滤指定条件的重复值

fun distinctUntilChangedByTest(){
        viewModelScope.launch {
            flowOf(
                Product("cat",30.98),
                Product("cat",99.23),
                Product("cat",0.22)
            ).distinctUntilChangedBy { it.name }.collect {
                Log.d(TAG, "distinctUntilChangedByTest:distinctUntilChangedBy $it")
            }
        }
    }
输出:Product(name=cat, price=30.98)
  • drop

返回一个flow,丢弃前count个数的元素

fun dropTest(){
        viewModelScope.launch {
            flowOf(1,2,3,4).drop(2).collect {
                Log.d(TAG, "dropTest:drop $it")
            }
        }
    }
输出:3,4
  • dropWhile

测试结果:给定条件和第一个元素匹配,匹配成功将后面所有的都返回,匹配失败返回所有。

条件==1
 fun dropWhileTest(){
        viewModelScope.launch {
            flowOf(1,2,3,4,5,6).dropWhile { it == 1 }.collect {
                Log.d(TAG, "dropWhileTest:dropWhile $it")
            }
        }
    }
输出:2,3,4,5,6
条件==3
fun dropWhileTest(){
        viewModelScope.launch {
            flowOf(1,2,3,4,5,6).dropWhile { it == 3 }.collect {
                Log.d(TAG, "dropWhileTest:dropWhile $it")
            }
        }
    }
输出:1,2,3,4,5,6
  • emitAll

将指定Channel中的所有数据发送到flow中并关闭

private fun initChannelData(){
        viewModelScope.launch {
            for (i in 1..5){
                emitAllChannel.send("hello$i")
            }
        }
    }
 fun emitAllTest(){
        viewModelScope.launch {
            flow {
                emitAll(emitAllChannel)
            }.collect {
                Log.d(TAG, "emitAllTest:emitAll $it")
            }
        }
    }
输出:hello1,hello2,hello3,hello4,hello5
  • filter

过滤给定条件的数据,返回flow

fun filterTest(){
        viewModelScope.launch {
            flowOf("A","B","C","D","b","c")
                .filter {
                    it == "A"
                }.collect {
                    Log.d(TAG, "filterTest:filter $it")
                }
        }
    }
输出:A
  • filterIsInstance

过滤给定类型的数据,返回flow

fun filterIsInstanceTest() {
        viewModelScope.launch {
            flowOf(1,"2",3,"4")
                .filterIsInstance()
                .collect {
                    Log.d(TAG, "filterIsInstanceTest:filterIsInstance $it")
                }
        }
    }
其中String是给定的类型
输出:2,4
  • filterNot

查找不符合条件的所有数据。返回flow,和filter相反

fun filterNotText(){
        viewModelScope.launch {
            flowOf(1,2,3,4).filterNot {
                it == 2
            }.collect {
                Log.d(TAG, "filterNotText:filterNot $it")
            }
        }
    }
输出:1,2,3
  • filterNotNull

过滤掉null值,返回flow

fun filterNotNullTest(){
        viewModelScope.launch {
            flowOf(1,null,3,"hello")
                .filterNotNull()
                .collect {
                Log.d(TAG, "filterNotNullTest:filterNotNull $it")
            }
        }
    }
输出:1,3,hello
  • first

返回flow中的第一个元素,并且结束flow的终端运算符,如果是空就会抛出异常

fun firstTest(){
        viewModelScope.launch {
            val result = flowOf(1, 2, 3, 3)
                .first()
            Log.d(TAG, "firstTest:first $result")
        }
    }
输出:1
  • firstOrNull

和first一样,firstOrNull可以为空

  • flatMapConcat

这是一个组合操作符,相当于 map + flattenConcat, 通过 map 转成一个流,在通过 flattenConcat展开合并成一个流

  • flatMapLatest

和其他 带 Latest的操作符 一样,如果下个值来了,上变换还没结束,就取消掉。

fun flatMapLatestTest(){
        viewModelScope.launch {
            flow {
                emit("a")
                delay(100)
                emit("b")
            }.flatMapLatest {
                flow {
                    emit(it)
                    delay(200)
                    emit(it+"_last")
                }
            }.collect {
                Log.d(TAG, "flatMapLatestTest:flatMapLatest $it")
            }
        }
    }
输出:a,b,b_last
  • flatMapMerge

该运算符连续调用transforms,然后合并结果流,并对并发收集的流的数量进行并发限制,这是map(transform).flattMerge(concurrency)的快捷方式

 fun flatMapMergeText(){
        viewModelScope.launch {
            flowOf(1,2,3,4,5)
                .flatMapMerge(3){
                    flow {
                        emit(it)
                    }
                }.collect {
                    Log.d(TAG, "flatMapMergeText:flatMapMerge $it")
                }
        }
    }
输出:1,2,3,4,5
  • flattenConcat

按顺序将给定的flow展开为单个flow

fun flattenConcatTest(){
        viewModelScope.launch {
            flow {
                emit(flowOf(1,2,3))
                emit(flowOf(4,5,6))
            }.flattenConcat().collect {
                Log.d(TAG, "flattenConcatTest:flattenConcat $it")
            }
        }
    }
输出:1,2,3,4,5,6
  • flattenMerge

和flattenConcat功能类似,参数是限制并发数量,concurrency=1时和flattenConcat一样,大于1时并发收集

fun flattenMergeTest(){
        viewModelScope.launch {
            flow {
                emit(flowOf(1,2,3).flowOn(Dispatchers.IO))
                emit(flowOf(4,5,6).flowOn(Dispatchers.IO))
                emit(flowOf(7,8,9).flowOn(Dispatchers.IO))
            }.flattenMerge(3).collect {
                Log.d(TAG, "flattenMergeTest:flattenMerge $it")
            }
        }
    }
输出:1,2,3,7,8,9,4,5,6输出的顺序不固定
  • fold

从初始值开始遍历根据给定规则变换值,并将结果作为下次执行的参数
官方翻译是累加值

fun foldTest(){
        viewModelScope.launch {
            val result = flowOf(2,3,4)
                .fold(1) { beforeResult, flowValue ->
                    beforeResult * flowValue
                }
            Log.d(TAG, "foldTest:fold $result")
        }
    }
输出:24,计算步骤1*2=2,2*3=6,6*4=24
  • reduce

和fold差不多,无初始值

  • last

获取flow最后一个元素,如果flow为空抛出NoSuchElementException

  • lastOrNull

返回流发出的最后一个元素的终端运算符,如果流为空,则返回null

  • launchIn

launchIn()是viewModelScope.launch{flow.collect()}的简写,launchIn()指定flow收集操作的作用域

fun launchInTest(){
        viewModelScope.launch {
            withContext(Dispatchers.IO){
                //IO线程
                flow {
                    emit(1)
                }.onEach {
                    Log.d(TAG, "launchInTest:launchIn ${Thread.currentThread().name}")
                }.launchIn(viewModelScope)//指定作用域执行
            }
        }
    }
输出:main,是主线程,flow整体在IO线程里面launchIn指定了viewModelScope作用域
  • map

将flow发出的值根据给定规则转换然后发出,lambda的返回值是最终发出的值

fun mapTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                emit(4)
            }.map {
                it * 2
            }.collect {
                Log.d(TAG, "mapTest:map $it")
            }
        }
    }
输出:2,8
  • mapLatest

和map类似,区别在于,当发送新当值当时候如果上次的转换还没结束将会取消上次的转换

fun mapLatest(){
        viewModelScope.launch {
            flow {
                emit(1)
                delay(100)
                emit(4)
            }.mapLatest {
                delay(200)
                it * 2
            }.collect {
                Log.d(TAG, "mapLatest: $it")
            }
        }
    }
输出:8,忽略了第一次的转换
  • mapNotNull

和map类似,区别在于mapNotNull过滤掉空值

  • merge

将多个flow合并成一个flow,不保留元素的顺序

fun mergeTest() {
        viewModelScope.launch {
            val oneFlow = flowOf(1, 2)
            val twoFlow = flowOf("a", "b")
            val threeFlow = flowOf("x", "y")
            listOf(oneFlow, twoFlow, threeFlow).merge().collect {
                Log.d(TAG, "mergeTest: $it")
            }
        }
    }
输出:1,2,a,b,x,y
  • onCompletion

当取消或结束的时候调用
相当与把flow包装到异常捕获的finally块中,一定会执行

fun onCompletionTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                emit(2)
                delay(300)
                emit(3)
            }.onCompletion {
                Log.d(TAG, "onCompletionTest: 完成")
            }.collect {
                Log.d(TAG, "onCompletionTest: $it")
            }
        }
    }
输出:1,2,3,完成
  • onEach

上游数据向下游发送之前调用

fun onEachTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                emit(2)
                delay(300)
                emit(3)
            }.onEach {
                Log.d(TAG, "onCompletionTest: $it")
            }.collect()
        }
    }
输出:1,2,3
  • onEmpty

当流完成却没有发出任何元素时回调。 可以用来兜底。

fun onEmptyTest() {
        viewModelScope.launch {
            emptyFlow()
                .onEmpty {
                    Log.d(TAG, "onEmptyTest: onEmpty")
                    emit(1)
                }.collect {
                    Log.d(TAG, "onEmptyTest: $it")
                }
        }
    }
输出:onEmpty,1
  • onStart

当上游开始发送数据之前调用

fun onStartTest(){
        viewModelScope.launch {
            flow {
                emit("a")
                emit("b")
            }.onStart {
                emit("start")
            }.collect{
                Log.d(TAG, "onStartTest: $it")
            }
        }
    }
输出:start,a,b
  • produceIn

将flow转换成ReceiveChannel

fun produceInTest(){
        viewModelScope.launch {
            flowOf(1,2,3).produceIn(this).consumeEach {
                Log.d(TAG, "produceInTest: $it")
            }
        }
    }
输出:1,2,3
  • retry

重试机制,当flow发生异常时可以重试

fun retryTest() {
        viewModelScope.launch {
            flow {
                Log.d(TAG, "produceInTest: 主动发出异常")
                throw IOException()
            }.retry(3){ e ->
                //筛选什么情况下重试,true重试,false不重试,默认是true
                val b = e is IOException
                b
            }.catch {
                Log.d(TAG, "produceInTest: 扑捕获异常")
            }.collect()
        }
    }
输出:主动发出异常,主动发出异常,扑捕获异常
  • retryWhen

有条件的进行重试

fun retryWhenTest() {
        viewModelScope.launch {
            flow {
                Log.d(TAG, "retryWhenTest: 主动发出异常")
                throw IOException()
            }.retryWhen { cause, attempt ->
                //attempt重试的次数,从0开始
                Log.d(TAG, "retryWhenTest: $attempt")
                if (attempt > 2) {
                    false
                } else {
                    cause is IOException
                }
            }.catch {
                Log.d(TAG, "retryWhenTest: 扑捕获异常")
            }.collect()
        }
    }
输出:主动发出异常,0,主动发出异常,1,主动发出异常,2,主动发出异常,3,扑捕获异常
  • runningFold

区别于 fold ,就是返回一个新流,将每步的结果发射出去。

  • runningReduce

区别于 reduce ,就是返回一个新流,将每步的结果发射出去。

  • sample

返回一个flow,在指定周期内,收集原始flow发出的最新值

fun sampleTest() {
        viewModelScope.launch {
            flow {
                repeat(10) {
                    emit(it)
                    delay(100)
                }
            }.sample(200).collect {
                Log.d(TAG, "sampleTest: $it")
            }
        }
    }
输出:1,3,5,7,9
  • scan

和 fold 相似,区别是fold 返回的是最终结果,scan返回的是个flow ,会把初始值和每一步的操作结果发送出去。

  • single

只收集一个值,如果为空或游多个值都会抛出异常

  • singleOrNull

接收流发送的第一个值 ,可以为空 ,发出多值的话除第一个,后面均被置为null

  • take

收集前n个数据

fun takeTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                emit(2)
                emit(3)
            }.take(2).collect {
                Log.d(TAG, "takeTest: $it")
            }
        }
    }
输出:1,2
  • takeWhile

也是找第一个不满足条件的项,但是取其之前的值 ,和dropWhile 相反。

fun takeWhileTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                emit(2)
                emit(3)
                emit(4)
            } .takeWhile { it <3  } .collect {
                Log.d(TAG, "takeTest: $it")
            }
        }
    }
输出:1,2
  • toCollection

将flow收集到集合中

 fun toCollectionTest(){
        val array = arrayListOf()
        viewModelScope.launch {
            flow {
                emit(1)
                emit(2)
                emit(3)
                emit(4)
            }.toCollection(array)
        }
        array.forEach {
            Log.d(TAG, "toCollectionTest: $it")
        }
    }
输出:1,2,3,4
  • toList

和toCollection类似,将flow结果收集到list中,参数可以指定一个list

fun toListTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                emit(2)
                emit(3)
                emit(4)
            }.toList().forEach {
                Log.d(TAG, "toListTest: $it")
            }
        }
    }
输出:1,2,3,4
  • toSet

将flow结果收集到set中

  • transform

转换flow发出的值

fun transformTest(){
        viewModelScope.launch {
            flow {
                emit(1)
                emit(2)
            }.transform{ value ->
                if (value == 1){
                    emit("One$value")
                }
                emit("other$value")
            }.collect {
                Log.d(TAG, "transformTest: $it")
            }
        }
    }
输出:One1,other1,other2
  • transformLatest

和transform类似,区别在于发出新值的时候,上次的转换还没结束就会取消

  • transformWhile

transformWhile转换函数中最后一行如果是true则应用转换函数,如果是false将不应用

  • withIndex

将flow结果包装成带有索引的flow,索引从0开始

fun withIndexTest(){
        viewModelScope.launch {
            flow {
                emit("a")
                emit("b")
            } .withIndex().collect {
                Log.d(TAG, "withIndexTest: ${it.index},${it.value}")
            }
        }
    }
输出:(0,a), (1,b)
  • zip

对两个flow进行组合,分别从二者取值,一旦一个流结束了,那整个过程就结束了

 fun zipTest(){
        viewModelScope.launch {
            val flow = flowOf(1, 2, 3).onEach { delay(10) }
            val flow2 = flowOf("a", "b", "c", "d").onEach { delay(15) }
            flow.zip(flow2) { i, s -> i.toString() + s }.collect {
                Log.d(TAG, "zipTest: $it")
            }
        }
    }
输出:1a,2b,3c

你可能感兴趣的:(kotlin flow (二))