在 Kotlin Coroutines 中,MutableStateFlow 是用于管理状态的核心 Hot Flow。更新状态主要有 value 赋值 、emit 、update 、getAndUpdate 、updateAndGet 和 compareAndSet 等方法。它们的核心区别在于 并发安全性 、原子性 和 返回值。
一、update 函数详解
作用 :基于旧值 计算新值 ,并通过 CAS (Compare-And-Swap) 机制保证原子性 与线程安全 ,彻底解决竞态条件 (Race Condition) 。
用法:
ini
// 1. 基本数据类型(计数)
_counter.update { prevCount -> prevCount + 1 }
// 2. 数据类(更新状态对象,必须使用.copy())
_uiState.update { currentState ->
currentState.copy(
isLoading = false,
data = newData
)
}
底层原理(伪代码) :
kotlin
kotlin
public inline fun <T> MutableStateFlow<T>.update(transform: (T) -> T) {
while (true) {
val prevValue = value // 1. 读取当前值
val nextValue = transform(prevValue) // 2. 基于旧值计算新值
if (compareAndSet(prevValue, nextValue)) { // 3. 原子比较并替换
return // 成功则退出,失败则循环重试
}
}
}
- 核心优势 :读取 - 修改 - 写入 三步操作是原子不可分割的。多协程 / 多线程并发修改时,不会丢失更新。
二、其他核心更新函数对比
1. value 赋值 (直接 Setter)
-
语法 :
mutableStateFlow.value = newValue -
特点:
- 非原子 :先读 (
value) 后写是两步独立操作。 - 简单直接 :适合与旧值无关的覆盖性更新。
- 并发风险 :多协程同时
value = value + 1会导致结果错误。
- 非原子 :先读 (
-
适用场景:状态重置、初始化、或单协程修改。
2. emit(value: T) (挂起函数)
-
语法 :
suspend fun emit(value: T) -
特点:
- 内部实现就是
value = newValue。 - 是 挂起函数,必须在协程中调用。
- 主要为了兼容
Flow接口的通用写法。
- 内部实现就是
-
适用场景 :需要与其他
Flow操作符(如transform)混用,或强调语义为 "发射事件" 时。
3. getAndUpdate
-
语法 :
fun getAndUpdate(transform: (T) -> T): T -
特点:
- 与
update一样原子安全。 - 返回值 :返回更新前的旧值。
- 与
-
适用场景:需要在更新的同时,获取并使用旧值进行日志记录或其他计算。
4. updateAndGet
-
语法 :
fun updateAndGet(transform: (T) -> T): T -
特点:
- 与
update一样原子安全。 - 返回值 :返回更新后的新值。
- 与
-
适用场景:需要立即使用刚刚更新成功的新值。
5. compareAndSet(expect: T, update: T): Boolean
-
语法 :
fun compareAndSet(expect: T, update: T): Boolean -
特点:
- 底层原语 :
update系列函数的基石。 - 逻辑 :仅当当前值 等于
expect时,才将其设置为update。 - 返回值 :
true(成功) /false(失败)。
- 底层原语 :
-
适用场景 :需要手动控制更新条件,实现更复杂的并发逻辑。
三、总对比表
| 函数 / 操作 | 并发安全 | 原子性 | 返回值 | 基于旧值 | 适用场景 |
|---|---|---|---|---|---|
value = x |
❌ 不安全 | ❌ 非原子 | Unit |
❌ 不依赖 | 单协程、覆盖赋值、重置 |
emit(x) |
❌ 不安全 | ❌ 非原子 | Unit |
❌ 不依赖 | 兼容 Flow 接口、挂起上下文 |
update { ... } |
✅ 安全 | ✅ 原子 | Unit |
✅ 是 | 首选! 所有基于旧值的计算(如计数、状态修改) |
getAndUpdate { ... } |
✅ 安全 | ✅ 原子 | 旧值 | ✅ 是 | 需要使用旧值 |
updateAndGet { ... } |
✅ 安全 | ✅ 原子 | 新值 | ✅ 是 | 需要立即使用新值 |
compareAndSet(e, u) |
✅ 安全 | ✅ 原子 | Boolean |
✅ 是 | 底层控制、复杂条件更新 |
四、最佳实践建议
-
优先使用
update {}:只要新值依赖旧值(new = f(old)),无论是否并发 ,都用update。它是最安全、最简洁的选择。kotlin
ini// 推荐 _uiState.update { it.copy(isLoading = true) } // 不推荐(有竞态风险) // _uiState.value = _uiState.value.copy(isLoading = true) -
使用数据类 +
copy():StateFlow按 值相等 (equals()) 发射。修改对象属性时,必须生成新实例才能通知观察者。 -
value仅用于赋值 :value属性主要用于直接读取当前值 或无条件覆盖。
五、代码示例
kotlin
kotlin
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
data class UiState(val count: Int = 0, val isLoading: Boolean = false)
fun main() = runBlocking {
val _uiState = MutableStateFlow(UiState())
// 1. 使用 update (最推荐)
launch {
repeat(5) {
_uiState.update { it.copy(count = it.count + 1) }
delay(100)
}
}
// 2. 使用 getAndUpdate (获取旧值)
launch {
delay(250)
val oldState = _uiState.getAndUpdate { it.copy(isLoading = true) }
println("更新前状态: $oldState")
}
// 3. 使用 updateAndGet (获取新值)
launch {
delay(300)
val newState = _uiState.updateAndGet { it.copy(isLoading = false) }
println("更新后状态: $newState")
}
// 监听结果
_uiState.collect {
println("当前状态: $it")
}
}