Kotlin协程-Flow

前言

Flow是kotlin协程中的流。RxJava就是流式编程的库。Flow属于冷流对应RxJava中的Observable Flowable Single MayBe和Completable等。Kotlin协程中的热流实现MutableSharedFlow和MutableStateFlow等,对应RxJava中热流PublisherSubject和BehaviorSubject。

  • 冷流:较少的访问和修改
  • 热流:频繁地读取和更新

Flow使用

kotlin 复制代码
fun main() {
    runBlocking (Dispatchers.Default){
        // 发送10个元素,从0到9
        val myFlow = flow {
            repeat(10){
                emit(it)
            }
        }
        launch {
            myFlow.collect{
                println("Coroutine1:$it")
            }
        }
        launch {
            myFlow.collect{
                println("Coroutine2:$it")
            }
        }
    }
}

协程1和2通过Flow.collect订阅Flow。

kotlin 复制代码
fun main() {
    runBlocking (Dispatchers.Default){
        // 发送10个元素,从0到9
        val myFlow = flow {
            repeat(10){
                // 修改原来的CoroutineContext,会异常
                withContext(Dispatchers.IO){
                    emit(it)
                }
            }
        }
        launch {
            myFlow.collect{
                println("Coroutine1:$it")
            }
        }
    }
}

Flow限制,不能修改原来的CoroutineContext。可以使用ChannelFlow就能正常使用。

kotlin 复制代码
fun main() {
    runBlocking (Dispatchers.Default){
        // 发送10个元素,从0到9
        val myFlow = channelFlow {
            repeat(10){
                // 可以修改原来的CoroutineContext
                withContext(Dispatchers.IO){
                    channel.send(it)
                }
            }
        }
        launch {
            myFlow.collect{
                println("Coroutine1:$it")
            }
        }
    }
}
kotlin 复制代码
fun main() {
    runBlocking(Dispatchers.Default) {
        // 发送10个元素,从0到9
        val myFlow = flow {
            repeat(10) {
                try {
                    emit(it)
                } catch (e: Throwable) {
                    emit(22)
                }
            }
        }
        launch {
            myFlow.collect {
                if (it == 2) {
                    // 这里出现异常后,collect订阅就结束了(只打印2次,第三次就异常了)
                    error("Error")
                }
                println("Coroutine1:$it")
            }
        }
    }
}

Flow中collect异常,那么订阅就结束了。

Flow工作原理

kotlin 复制代码
public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)

private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
    override suspend fun collectSafely(collector: FlowCollector<T>) {
        collector.block()
    }
}

public final override suspend fun collect(collector: FlowCollector<T>) {
    val safeCollector = SafeCollector(collector, coroutineContext)
    try {
        collectSafely(safeCollector)
    } finally {
        safeCollector.releaseIntercepted()
    }
}

创建SafeFlow,继承于AbstractFlow,订阅调用的是collect。检查当前CoroutineContext和调用的collect方法传入的是否一致,不一致就抛出异常。 Flow被SafeCollector代理去检查异常。 转换前的流称上游Upstream,处理后再发送到下游Downstream

flatMap操作符

类似于RxJava中的concatMap操作符

kotlin 复制代码
fun main() {
    runBlocking(Dispatchers.Default) {
        val myFlow = flow {
            repeat(3) {
                emit(it)
            }
        }
        launch {
            // 将原来的流元素构建成一个新的流(按照原来的流元素输出)
            myFlow.flatMapConcat { upstreamValue ->
                flow {
                    delay(1000L - upstreamValue * 100)
                    repeat(2) {
                        emit(upstreamValue * 10 + it)
                    }
                }
            }.collect {
                println("collect $it")
            }
        }
    }
}
输出:
collect 0
collect 1
collect 10
collect 11
collect 20
collect 21

将原来发送3个元素,通过flatMapConcat()发送两个元素。越是先发送的元素延迟时间越长,然后按顺序输出6个元素。

flatMapMerge

类似于RxJava中的flatMap

kotlin 复制代码
fun main() {
    runBlocking(Dispatchers.Default) {
        val myFlow = flow {
            repeat(3) {
                emit(it)
            }
        }
        launch {
            // 将原来的流元素构建成一个新的流(默认并发16个)谁先执行完就发送谁
            myFlow.flatMapMerge { upstreamValue ->
                flow {
                    delay(1000L - upstreamValue * 100)
                    repeat(2) {
                        emit(upstreamValue * 10 + it)
                    }
                }
            }.collect {
                println("collect $it")
            }
        }
    }
}
输出:
collect 20
collect 21
collect 10
collect 11
collect 0
collect 1

不会保证原来的顺序,哪个流先处理完就先发送数据。concurrency默认值16,并行执行的数量。当concurrency为1时和flatMapConcat一样。

kotlin 复制代码
fun main() {
    runBlocking(Dispatchers.Default) {
        val myFlow = flow {
            repeat(3) {
                emit(it)
            }
        }
        launch {
            // 将原来的流元素构建成一个新的流(并发数是2,达到2个的时候等待然后再执行下一个)
            myFlow.flatMapMerge(2) { upstreamValue ->
                flow {
                    delay(1000L - upstreamValue * 100)
                    repeat(2) {
                        emit(upstreamValue * 10 + it)
                    }
                }
            }.collect {
                println("collect $it")
            }
        }
    }
}
输出:
collect 10
collect 11
collect 0
collect 1
collect 20
collect 21

flatMapLatest

类似于RxJava中的switchMap

kotlin 复制代码
fun main() {
    runBlocking(Dispatchers.Default) {
        val myFlow = flow {
            repeat(3) {
                emit(it)
            }
        }
        launch {
            // 前面没执行完的Flow会被取消,然后被后续的Flow替换
            myFlow.flatMapLatest  { upstreamValue ->
                flow {
                    delay(1000L - upstreamValue * 100)
                    repeat(2) {
                        emit(upstreamValue * 10 + it)
                    }
                }
            }.collect {
                println("collect $it")
            }
        }
    }
}
输出:
collect 20
collect 21

总结

介绍了Flow常用的操作符map flatMap(串式) flatMapMerge(并发) flatmapLatest(取代旧的)等简单使用。

相关推荐
Mr数据杨5 小时前
【Codex】用学生入学评估模块建立新生能力画像
android·java·javascript·django·codex·项目开发
●VON6 小时前
谁小时候不想要一个游戏修改器?我把电脑 Skill 做成了 Android 小助手
android·游戏·电脑
刃神太酷啦6 小时前
Linux 系统 MySQL 完整安装配置教程:从卸载 MariaDB 到优化 my.cnf----《Hello MySQL!》(1)
android·linux·c语言·c++·mysql·leetcode·mariadb
里欧跑得慢6 小时前
Flutter 三方库 function_tree — 鸿蒙应用开发中的动态数学公式解析与计算神器,实现鸿蒙深度适配下的复杂函数逻辑运行时求值实战(适配鸿蒙 HarmonyOS Next ohos)
android·前端·安全·flutter·华为·harmonyos
IT毕设实战小研6 小时前
基于安卓的空气质量查询系统
android·大数据·vue.js·爬虫·python·django·课程设计
雨声不在9 小时前
docker-android Android 16 支持与编译修改
android
天空之城--14 小时前
【无标题】
android
mmsx15 小时前
osmdroid 接入谷歌影像底图:瓦片 URL 拼接与多域名轮询实战
android·osmdroid
ZYJCSZKJ16 小时前
基于大语言模型的地域实体语义增强:生成式引擎优化(GEO)中的多模态内容生成实践
android·人工智能·语言模型
ssshooter16 小时前
Tauri + GitHub Actions 自动化发布 Android APK 技术总结
android·github·android studio