这些flow常见API的使用,你一定需要掌握!(三)

1,244 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

本篇文章将是介绍flow一些常见API系列的第三篇文章,希望能够帮助大家更好的掌握flow使用,熟练的应用于各种场景。

历史文章

这些flow常见API的使用,你一定需要掌握!(一)
这些flow常见API的使用,你一定需要掌握!(二)
这些flow常见API的使用,你一定需要掌握!(四)
这些flow常见API的使用,你一定需要掌握!(五)

flatMapLatest{}展平并丢弃上个未执行完毕的流

public inline fun <T, R> Flow<T>.flatMapLatest(@BuilderInference crossinline transform: suspend (value: T) -> Flow<R>): Flow<R> =
    transformLatest { emitAll(transform(it)) }

这个扩展函数非常好用,当收到下一个流时,会取消上一次未执行完毕的流。举个在Android中的应用例子:

当通过搜索框输入字符实时搜索时,上一次搜索还没处理完毕前收到了下一次搜索请求,此时就自动会把上一次未处理完毕的搜索请求取消掉。

接下来看个例子:

fun flat() {
    GlobalScope.launch {
        flowOf(
            flow {
                println("flow1")
                delay(1000)
                emit(1)
            },
            flow {
                println("flow2")
                delay(2000)
                emit(2)
            }).flatMapLatest {
            flow {
                it.collect {
                    emit("haha".repeat(it))
                }
            }
        }.collect {
            println("flat collect: $it")
        }
    }
}

输出结果如下:

image.png

可以看到flow2代码块中休眠了2s而flow1只休眠了1s,所以flow1会执行较快,所以当flow1到达处理时而flow2未执行完毕就会被取消掉,最终的结果也只输出了flow1对应的执行结果。

distinctUntilChanged()去重处理

public fun <T> Flow<T>.distinctUntilChanged(): Flow<T> =
    when (this) {
        is StateFlow<*> -> this // state flows are always distinct
        else -> distinctUntilChangedBy(keySelector = defaultKeySelector, areEquivalent = defaultAreEquivalent)
    }

最关键的是调用了distinctUntilChangedBy函数,该方法接受两个参数,用来定义去重的规则,我们也可以自定义去重规则。

fun flat() {
    GlobalScope.launch {
        flow {
            println("flow2")
            for (i in 0..10) {
                emit(10)
            }
        }
            .distinctUntilChanged()
            .collect {
                println("flat collect: $it")
            }
    }
}

输出:

image.png

takeWhile{}是否中止流

public fun <T> Flow<T>.takeWhile(predicate: suspend (T) -> Boolean): Flow<T> = flow {
    return@flow collectWhile { value ->
        if (predicate(value)) {
            emit(value)
            true
        } else {
            false
        }
    }
}

这个方法也非常好用,接受一个返回值为布尔类型的函数类型,其中返回true则继续执行流的执行传递,返回false将直接中止后续流的执行。这里也用一个Android的例子说明使用场景:

加载某个列表数据需要同时从本地数据库和网路进行请求,如果网络请求响应快于本地数据库读取,此时当网络响应的结果收到时,直接中断后续流的执行(本地数据库的)。

fun flat() {
    GlobalScope.launch {
        flow {
            for (i in 0..10) {
                emit(i)
            }
        }
            .takeWhile {
                it in 0..5
            }
            .collect {
                println("flat collect: $it")
            }
    }
}

看下执行结果:

image.png

可以看到当打印到5时就不再输出了,也就说后续的流被中止了。

take(n)取前n条数据

public fun <T> Flow<T>.take(count: Int): Flow<T> {
    return flow {
        var consumed = 0
        try {
            collect { value ->
                if (++consumed < count) {
                    return@collect emit(value)
                } else {
                    return@collect emitAbort(value)
                }
            }
        } catch (e: AbortFlowException) {
            e.checkOwnership(owner = this)
        }
    }
}

这个方法和takeWhile{}有点像,不过就是取前n条数据后就中止流的执行,这个场景也挺常见,就不再举例。使用takeWhilt{}的例子调用take(4)的执行效果如下:

image.png