在 Kotlin 中,LiveData 和 Flow 是 Android 开发中核心的异步数据处理工具,二者的数据发送/发射方法因设计理念(LiveData 面向生命周期、Flow 面向协程异步流)不同而差异显著。下面分模块梳理它们的发送/发射方法,并对比核心区别。
一、LiveData 的数据发送方法
LiveData 是 Jetpack 提供的生命周期感知型可观察数据持有者 ,核心是向观察者(Observer)发送数据,主要发送方法有 3 类,且均为线程安全(内部通过 MainThread 调度)。
1. setValue(T) - 主线程发送(必须)
- 用法 :直接调用
liveData.setValue(data),仅能在主线程(UI 线程) 执行。 - 行为:立即将数据分发给活跃状态(Resumed/Started)的观察者,若观察者非活跃则暂存数据,待其活跃后补发。
- 异常 :若在子线程调用,会抛出
IllegalStateException。
2. postValue(T) - 线程无关发送(异步)
- 用法 :调用
liveData.postValue(data),可在任意线程执行。 - 行为 :
- 内部通过
Handler将数据发送任务切换到主线程执行; - 若短时间内多次调用,仅最后一次的值会生效(中间值被合并);
- 数据分发时机为下一次主线程消息循环,属于异步操作。
- 内部通过
- 场景:子线程更新 LiveData(如网络请求、数据库查询后)。
3. emit()(LiveData 协程扩展)- 协程内发送
-
背景 :Jetpack 提供
lifecycle-livedata-ktx扩展,支持协程中通过emit发送数据。 -
用法 :通过
liveData { ... }构建器结合emit:kotlinval userLiveData = liveData { val user = repo.getUser() // 协程内执行异步操作 emit(user) // 发送数据(自动切换到主线程) emit(anotherUser) // 可多次发送 } -
行为 :
- 仅能在
liveData构建器的协程作用域内调用; - 自动切换到主线程分发数据,无需手动处理线程;
- 支持挂起函数,可等待异步操作完成后发送。
- 仅能在
LiveData 发送方法核心对比
| 方法 | 线程限制 | 执行时机 | 多次调用处理 | 适用场景 |
|---|---|---|---|---|
setValue |
仅主线程 | 同步 | 每次都分发 | 主线程即时更新 UI |
postValue |
任意线程 | 异步 | 合并为最后一次 | 子线程更新数据(非高频) |
emit |
协程内(任意) | 挂起同步 | 每次都分发 | 协程异步操作后发送数据 |
二、Flow 的数据发射方法
Flow 是 Kotlin 协程提供的冷流(Cold Stream) ,核心是通过 emit 系列方法发射数据,完全基于协程,需在挂起函数/协程作用域内执行,且本身不绑定线程(需通过 flowOn 指定发射线程)。
1. emit(T) - 基础发射(单次值)
-
用法 :在
flow { ... }构建器内调用emit(data),是 Flow 最核心的发射方法。kotlinval numberFlow = flow { for (i in 1..3) { delay(100) // 挂起函数,不阻塞线程 emit(i) // 发射单个值 } } -
行为 :
- 挂起函数,发射后挂起直到观察者接收;
- 冷流特性:仅当观察者(
collect)订阅时才开始发射; - 支持背压(Backpressure):根据观察者处理能力调整发射速率。
2. emitAll(Flow<T>) - 发射另一个流的所有值
-
用法 :在
flow构建器内,通过emitAll发射另一个 Flow 的全部数据:kotlinval parentFlow = flow { emit("开始") emitAll(childFlow) // 发射子流的所有值 emit("结束") } val childFlow = flow { emit(1); emit(2) } -
行为 :
- 挂起直到传入的 Flow 发射完成;
- 按顺序发射:先发射当前流的已有值,再发射子流的所有值,最后继续当前流。
3. tryEmit(T) - 非挂起发射(无背压)
-
用法 :在
MutableSharedFlow/MutableStateFlow(热流)中使用,非挂起函数:kotlinval sharedFlow = MutableSharedFlow<Int>() // 任意协程/挂起函数外(但仍需协程作用域) sharedFlow.tryEmit(1) -
行为 :
- 非挂起,立即尝试发射数据;
- 无背压:若观察者处理能力不足,可能直接失败(返回
false); - 仅适用于热流(SharedFlow/StateFlow) ,冷流
flow { }不支持。
4. StateFlow.value - 状态流赋值(特殊发射)
-
StateFlow 是特殊的 SharedFlow,代表"单一状态",通过
value属性赋值即发射数据:kotlinval stateFlow = MutableStateFlow(0) stateFlow.value = 1 // 发射新值(等同于发射) -
行为 :
- 仅当新值与旧值不同时才发射;
- 有初始值,始终持有最新值;
- 线程安全,可在任意线程赋值(但建议主线程更新 UI 相关状态)。
Flow 发射方法核心对比
| 方法 | 类型 | 是否挂起 | 背压支持 | 适用场景 |
|---|---|---|---|---|
emit |
冷流/热流 | 是 | 是 | 冷流基础发射,按需分发 |
emitAll |
冷流/热流 | 是 | 是 | 合并多个流的发射逻辑 |
tryEmit |
仅热流 | 否 | 否 | 非挂起场景,无需背压 |
StateFlow.value |
仅状态流 | 否 | 是 | 单一状态更新,UI 状态同步 |
三、LiveData vs Flow 发送/发射方法的核心区别
| 维度 | LiveData | Flow |
|---|---|---|
| 线程模型 | 强绑定主线程(setValue/postValue 自动切主线程) |
无线程绑定,通过 flowOn 指定发射线程,collect 指定接收线程 |
| 生命周期感知 | 天生支持(仅向活跃观察者发送) | 需结合 lifecycle.repeatOnLifecycle 实现生命周期感知 |
| 冷/热特性 | 半热(有数据时,活跃观察者立即接收) | 冷流(flow { })/热流(SharedFlow/StateFlow) |
| 背压支持 | 无(多次 postValue 会合并) |
冷流原生支持背压,热流可配置背压策略 |
| 挂起/非挂起 | setValue/postValue 非挂起,emit 挂起 |
核心 emit 挂起,tryEmit/value 非挂起 |
| 多次发送处理 | postValue 合并,setValue 即时 |
冷流按序发射,StateFlow 仅发射不同值 |
| 异常处理 | 无原生支持(异常会崩溃) | 支持 catch 操作符捕获异常 |
四、选型建议
- 简单 UI 状态更新 :优先用
StateFlow(替代 LiveData),结合repeatOnLifecycle感知生命周期; - 一次性异步请求(如网络) :用冷流
flow { }+emit,订阅后自动取消; - 跨页面/多观察者事件分发 :用
SharedFlow+tryEmit; - 兼容旧代码 :LiveData +
emit构建器,逐步迁移到 Flow。
总结:LiveData 的发送方法围绕"主线程、生命周期"设计,功能简单;Flow 的发射方法围绕"协程、流、背压"设计,更灵活且覆盖更多异步场景,是目前 Android 异步数据处理的主流选择。