Kotlin Flow 快速入门

如果你熟悉 RxJava,那么 Kotlin Flow 就是 Kotlin 协程世界的 RxJava;如果你不熟悉,没关系,你可以把 Flow 想象成一条可以暂停、恢复的"数据流水线"


1. 为什么需要 Flow?

在 Flow 出现之前,我们处理多个连续数据(比如每秒更新一次的倒计时,或者数据库的实时变化)通常很麻烦:

  • List/Sequence: 只能一次性返回所有数据,不能异步返回(会阻塞)。
  • LiveData: 只能在主线程运行,操作符支持有限,且主要用于 UI 层。
  • RxJava: 功能强大但学习曲线陡峭,且容易造成"回调地狱"。

Flow 是基于 Kotlin 协程 构建的,它天生支持挂起(Suspend),这意味着它可以在不阻塞线程的情况下,异步地生成和消费数据。


2. 流水线三部曲

使用 Flow 就像搭建一条工厂流水线:

  1. 上游 (Upstream) : 生产者,负责制造数据(发射 emit)。
  2. 中游 (Intermediaries) : 工人,负责加工数据(操作符 map, filter 等)。
  3. 下游 (Downstream) : 消费者,负责接收成品(收集 collect)。

2.1 最简单的例子

Kotlin 复制代码
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // 1. 上游:创建一个 Flow
    flow {
        println("开始生产...")
        emit(1) // 发射数据
        delay(100) // 模拟耗时,不会阻塞线程
        emit(2)
        emit(3)
    }
    // 2. 中游:操作符处理
    .filter { it % 2 != 0 } // 只保留奇数
    .map { "数字: $it" }     // 转换成字符串
    // 3. 下游:收集数据(末端操作符)
    .collect { value ->
        println("收到: $value")
    }
}

输出:

Kotlin 复制代码
开始生产...
收到: 数字: 1
收到: 数字: 3

3. 冷流 vs 热流 (Cold vs Hot)

这是面试和实战中最关键的概念。

3.1 普通 Flow 是"冷"的 (Cold)

特点 :只有当你调用 collect() 时,上游的代码才会执行。如果不收集,它就什么都不做。
比喻:像一张 CD 唱片。你不播放(collect),音乐就不会响;你每播放一次,音乐就从头开始放一遍。

3.2 StateFlow / SharedFlow 是"热"的 (Hot)

特点 :无论有没有人收集,它都在那里。通常用于状态管理
比喻:像直播电台。不管有没有听众,电台都在广播;你晚进来了,就只能听到之后的内容(或者最新的那一条)。

StateFlow (状态流)
  • 用途 :专门用于替代 LiveData,存储 UI 状态(如 UiState)。
  • 特性粘性 (Sticky)。新订阅者会立即收到当前最新的值。
  • 去重:如果新设置的值和旧值一样,不会触发更新。
Kotlin 复制代码
// ViewModel 中
private val _uiState = MutableStateFlow(LoginState()) // 必须有初始值
val uiState = _uiState.asStateFlow()

// 更新状态
_uiState.value = newState 
// 或者
_uiState.update { it.copy(isLoading = true) }
SharedFlow (共享流)
  • 用途:用于一次性事件(如 Toast、导航、SnackBar)。
  • 特性 :默认非粘性。事件发出去,如果当时没人在听,这事就过去了。
Kotlin 复制代码
// ViewModel 中
private val _events = MutableSharedFlow<String>()
val events = _events.asSharedFlow()

// 发送事件
viewModelScope.launch {
    _events.emit("登录成功")
}

4. 常用操作符实战

Flow 的强大在于丰富的操作符。

4.1 线程切换:flowOn

RxJava 里的 subscribeOn,在 Flow 里极其简单。

Kotlin 复制代码
flow {
    // 这里在 IO 线程执行(如下载文件)
    emit(downloadFile()) 
}
.flowOn(Dispatchers.IO) // 指定上游在 IO 线程
.collect { 
    // 这里在主线程执行(如果是在 UI 作用域启动的)
    updateUi(it) 
}

4.2 异常捕获:catch

不要用 try-catch 包裹 collect,而应该使用声明式的 catch 操作符。

Kotlin 复制代码
flow {
    emit(1)
    throw RuntimeException("出错啦")
}
.catch { e -> 
    emit(-1) // 发生错误时发射一个兜底值
    println("捕获到异常: ${e.message}") 
}
.collect { println(it) }

4.3 UI 防抖:debounce

防止用户疯狂点击搜索按钮,造成网络拥堵。

Kotlin 复制代码
searchQueryFlow
    .debounce(300) // 300ms 内没有新输入,才发送请求
    .flatMapLatest { query -> 
        // 自动取消旧的搜索请求,只处理最新的
        api.search(query) 
    }
    .collect { results -> show(results) }

5. Android / Compose 中的最佳实践

在 Android UI 中收集 Flow 时,必须注意生命周期安全。否则,当应用退到后台时,Flow 还在后台一直更新 UI,会导致崩溃或耗电。

在 Compose 中(推荐)

使用 collectAsStateWithLifecycle(需要引入 androidx.lifecycle:lifecycle-runtime-compose 库)。

Kotlin 复制代码
@Composable
fun UserScreen(viewModel: UserViewModel) {
    // 自动感知生命周期:APP 切后台时暂停收集,切回来恢复
    val state by viewModel.uiState.collectAsStateWithLifecycle()

    if (state.isLoading) {
        LoadingView()
    } else {
        ContentView(state.data)
    }
}

在传统 View 系统中

使用 repeatOnLifecycle

Kotlin 复制代码
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiState.collect { state ->
            // 更新 View
        }
    }
}

总结

  1. Flow 是处理异步数据流的利器,支持挂起函数。
  2. Cold Flow (普通 Flow)像 CD,按需启动;Hot Flow(StateFlow)像直播,状态常驻。
  3. ViewModel 中使用 StateFlow 管理状态,SharedFlow 管理事件。
  4. UI 层 收集时务必使用 collectAsStateWithLifecyclerepeatOnLifecycle 保证安全。
相关推荐
金融RPA机器人丨实在智能7 小时前
Android Studio开发App项目进入AI深水区:实在智能Agent引领无代码交互革命
android·人工智能·ai·android studio
科技块儿7 小时前
利用IP查询在智慧城市交通信号系统中的应用探索
android·tcp/ip·智慧城市
独行soc7 小时前
2026年渗透测试面试题总结-18(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
王码码20358 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
2501_915106328 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview
vistaup8 小时前
OKHTTP 默认构建包含 android 4.4 的TLS 1.2 以及设备时间不对兼容
android·okhttp
常利兵8 小时前
ButterKnife在Android 35 + Gradle 8.+环境下的适配困境与现代化迁移指南
android
撩得Android一次心动8 小时前
Android LiveData 全面解析:使用Java构建响应式UI【源码篇】
android·java·android jetpack·livedata
熊猫钓鱼>_>8 小时前
移动端开发技术选型报告:三足鼎立时代的开发者指南(2026年2月)
android·人工智能·ios·app·鸿蒙·cpu·移动端
Rainman博18 小时前
WMS-窗口relayout&FinishDrawing
android