Kotlin Flow 完全指南
一、Flow 到底是什么
一句话:Flow = Kotlin 的"异步数据流"
它是:协程版 RxJava
但:
- 更轻量
- 更简单
- 更符合 Kotlin 风格
你可以把 Flow 理解成:"会持续发数据的管道"
比如:
- 网络请求状态
- 输入框搜索
- 倒计时
- WebSocket
- 数据库变化
- 页面状态更新
这些都不是:只返回一次 ,而是:持续产生数据
这就是:Flow 的核心
二、先用最容易理解的例子
普通函数
kotlin
fun getData(): String {
return "hello"
}
只能返回一次:hello → 结束
Flow
kotlin
fun getFlow() = flow {
emit("hello")
delay(1000)
emit("world")
}
它会:hello → 1秒后 → world
也就是:可以连续发送数据
三、最核心的两个东西
Flow 只有两个关键点:
| 方法 | 作用 |
|---|---|
emit() |
发送数据 |
collect() |
接收数据 |
kotlin
flow.collect { }
四、第一个完整 Flow
创建
kotlin
val flow = flow {
emit(1)
emit(2)
emit(3)
}
收集
kotlin
lifecycleScope.launch {
flow.collect {
println(it)
}
}
输出:
1
2
3
五、为什么必须在协程里 collect
因为:Flow 是挂起的
collect 本质是 suspend fun collect()
所以必须:launch 或在 suspend 函数里调用。
六、Flow 执行流程(超重要)
很多人学不会就是这里没懂。
Flow 是"冷流" ------ 你不 collect,它就不执行。
kotlin
val flow = flow {
println("开始")
emit(1)
}
此时:什么都不会打印,因为没人收集。
collect 后:
kotlin
flow.collect()
才会输出:开始 → 1
七、最经典 Android 场景
场景1:网络请求状态
这是实际项目最多的。
不使用 Flow
kotlin
fun login(callback: Callback)
回调地狱。
Flow 版本
kotlin
fun login() = flow {
emit(Loading)
val result = api.login()
emit(Success(result))
}
页面:
kotlin
lifecycleScope.launch {
viewModel.login().collect {
when(it) {
is Loading -> showLoading()
is Success -> showData()
}
}
}
这就是:MVVM + Flow,现代 Android 标配。
八、StateFlow(真正项目核心)
实际项目用最多的不是 Flow,而是:StateFlow
因为 UI 需要:
- 始终持有最新状态
九、StateFlow 最容易理解方式
它像:LiveData 升级版
但:
- 协程化
- 更强
- 更快
- 支持 Flow 全家桶
十、最经典 StateFlow
ViewModel
kotlin
class MainViewModel : ViewModel() {
private val _state = MutableStateFlow("默认值")
val state = _state.asStateFlow()
fun update() {
_state.value = "新数据"
}
}
页面监听
kotlin
lifecycleScope.launch {
viewModel.state.collect {
tv.text = it
}
}
StateFlow 最大特点:永远有值
kotlin
MutableStateFlow("默认值")
必须给初始值,因为它代表:当前状态
十一、为什么 Google 推荐 StateFlow 替代 LiveData
| 特性 | StateFlow | LiveData |
|---|---|---|
| 协程支持 | 强 | 弱 |
| 操作符 | 超多 | 很少 |
| Kotlin 化 | 完全 | 一般 |
| 冷热流支持 | 支持 | 不支持 |
| 性能 | 更强 | 一般 |
| 官方趋势 | 主推 | 逐渐弱化 |
十二、实际项目最常见结构:UIState
现代 Android 必会。
定义状态
kotlin
sealed class UIState {
object Loading : UIState()
data class Success(val data: String) : UIState()
data class Error(val msg: String) : UIState()
}
ViewModel
kotlin
private val _uiState = MutableStateFlow<UIState>(UIState.Loading)
val uiState = _uiState.asStateFlow()
// 更新状态
viewModelScope.launch {
try {
val result = api.getData()
_uiState.value = UIState.Success(result)
} catch (e: Exception) {
_uiState.value = UIState.Error("失败")
}
}
页面
kotlin
lifecycleScope.launch {
viewModel.uiState.collect {
when(it) {
is UIState.Loading -> { /* show loading */ }
is UIState.Success -> { /* show data */ }
is UIState.Error -> { /* show error */ }
}
}
}
十三、SharedFlow 是什么
一句话:可广播事件流
- StateFlow:保存状态
- SharedFlow:发送事件
十四、最经典场景:Toast
Toast:一次性事件,不应该保存状态。
SharedFlow
kotlin
private val _event = MutableSharedFlow<String>()
val event = _event
// 发送
_event.emit("登录成功")
// 页面
collect {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}
十五、为什么 Toast 不能用 StateFlow
因为 StateFlow 会记住最后一个值。
旋转屏幕后:Toast 又弹一次
这就是经典的 LiveData Event 问题,SharedFlow 可以避免。
十六、Flow 最重要操作符
map --- 数据转换
kotlin
flow.map { it * 2 }
filter --- 过滤
kotlin
flow.filter { it > 10 }
debounce(Android 超常用)--- 防抖
搜索框神器:
kotlin
editTextFlow.debounce(500)
// 停止输入500ms后才搜索
十七、搜索框实战(面试高频)
kotlin
editTextFlow
.debounce(500)
.distinctUntilChanged()
.collect { search(it) }
| 操作符 | 含义 |
|---|---|
debounce |
停止输入再搜索,避免疯狂请求 |
distinctUntilChanged |
内容没变不重复搜索 |
十八、flatMapLatest(巨重要)
这是:Flow 灵魂操作符
场景 :用户快速搜索 a → ab → abc → abcd,你希望只保留最后一次请求。
kotlin
queryFlow.flatMapLatest { repository.search(it) }
效果:
- 旧请求:自动取消
- 只保留:最新请求
十九、Android 搜索框完整实战
kotlin
searchFlow
.debounce(300)
.distinctUntilChanged()
.flatMapLatest { repository.search(it) }
.flowOn(Dispatchers.IO)
.collect { adapter.submitList(it) }
二十、flowOn 是什么
切线程:
kotlin
flow {
emit(api.getData())
}.flowOn(Dispatchers.IO)
表示:上游在 IO 线程
collect 默认在当前协程线程,通常是 Main。
二十一、catch 异常处理
kotlin
flow {
emit(api.getData())
}.catch {
emit(emptyList())
}
二十二、onStart --- 开始加载
kotlin
flow.onStart { showLoading() }
二十三、onCompletion --- 结束
kotlin
flow.onCompletion { hideLoading() }
二十四、Room 为什么天然支持 Flow
kotlin
@Query("SELECT * FROM users")
fun getUsers(): Flow<List<User>>
数据库变化:自动通知 UI
这是:响应式数据库
二十五、Compose 为什么和 Flow 天生搭配
- Compose:状态驱动 UI
- Flow:状态流
天然适配:
kotlin
val state by viewModel.state.collectAsState()
UI 自动刷新。
二十六、Flow vs LiveData
| 对比 | Flow | LiveData |
|---|---|---|
| 协程支持 | 强 | 弱 |
| 操作符 | 超多 | 很少 |
| Kotlin 化 | 完全 | 一般 |
| 冷热流支持 | 支持 | 不支持 |
| 性能 | 更强 | 一般 |
| 官方趋势 | 主推 | 逐渐弱化 |
二十七、Flow vs RxJava
| 对比 | Flow | RxJava |
|---|---|---|
| 学习成本 | 低 | 高 |
| Kotlin 支持 | 官方 | 一般 |
| 操作符数量 | 中等 | 超多 |
| Android 官方 | 主推 | 非主推 |
| 协程融合 | 原生 | 不自然 |
二十八、项目里真正怎么用
现代 Android 通常:
- Repository 返回 Flow:
fun getData(): Flow<Data> - ViewModel 转 StateFlow:
MutableStateFlow - UI collect
repeatOnLifecycle
二十九、repeatOnLifecycle(非常重要)
错误写法:
kotlin
launch {
flow.collect {}
}
页面退出可能还在收集。
正确写法:
kotlin
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
flow.collect { }
}
}
- 页面不可见:自动停止
- 页面回来:自动恢复
三十、Android 项目最佳实践结构
Repository
kotlin
fun getUser() = flow {
emit(api.getUser())
}
ViewModel
kotlin
val uiState = repository.getUser()
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
UIState.Loading
)
Activity/Fragment
kotlin
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { }
}
三十一、最容易记忆的一张图
Flow
├── 数据流
├── emit → 发数据
└── collect → 收数据
StateFlow → 保存状态
SharedFlow → 发送事件
flatMapLatest → 只保留最新请求
debounce → 防抖
flowOn → 切线程
三十二、面试高频回答模板
如果面试问:Flow 有什么用?
可以这样答:
Flow 是 Kotlin 协程提供的异步数据流,用于替代传统 Callback、LiveData、部分 RxJava。
Flow 支持:持续数据发送、协程挂起、操作符链式调用、线程切换、异常处理。
Android 项目中:通常 Repository 返回 Flow,ViewModel 使用 StateFlow 管理 UI 状态,SharedFlow 处理一次性事件。
常见场景:网络请求、搜索防抖、Room 数据监听、Compose 状态更新、WebSocket、分页加载。
Google 官方目前推荐:Flow + StateFlow 作为现代 Android 响应式方案。