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 保证安全。
相关推荐
Android-Flutter11 小时前
android compose PullToRefreshAndLoadMore 下拉刷新 + 上拉加载更多 使用
android·kotlin
似霰12 小时前
HIDL Hal 开发笔记4----Passthrough HALs 实例分析
android·framework·hal
louisgeek13 小时前
Git 查询 Tag 列表
android
诸神黄昏EX13 小时前
Android Safety 系列专题【篇二:keystore安全架构】
android
撩得Android一次心动13 小时前
Android 架构模式的演变(MVC、MVP、MVVM、MVI)
android·架构·mvc·mvvm·mvp
与水同流13 小时前
GNSS数据格式
android
Mars-xq14 小时前
Android godot 交互数据监听
android·godot·交互
_李小白14 小时前
【Android FrameWork】延伸阅读: PowerManagerService
android
_李小白14 小时前
【Android 性能分析】第四天:CPU Profiler
android