StateFlow 中update的用法

在 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 ✅ 是 底层控制、复杂条件更新

四、最佳实践建议

  1. 优先使用 update {} :只要新值依赖旧值(new = f(old)),无论是否并发 ,都用 update。它是最安全、最简洁的选择。

    kotlin

    ini 复制代码
    // 推荐
    _uiState.update { it.copy(isLoading = true) }
    // 不推荐(有竞态风险)
    // _uiState.value = _uiState.value.copy(isLoading = true)
  2. 使用数据类 + copy() :StateFlow 按 值相等 (equals()) 发射。修改对象属性时,必须生成新实例才能通知观察者。

  3. 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")
    }
}
相关推荐
hai_android2 天前
LiveEventBusCore 跨 Activity 通讯源码解析
android·kotlin·android jetpack
我命由我123455 天前
Android 广播 - 组件通信
android·java·开发语言·android studio·android jetpack·android-studio·android runtime
alexhilton6 天前
为什么你的 LazyColumn 仍然卡顿
android·kotlin·android jetpack
hai_android6 天前
Android 嵌套滑动冲突:内部拦截 vs 外部拦截实战
android·android studio·android jetpack
alexhilton9 天前
模块化Jetpack Compose架构
android·kotlin·android jetpack
hai_android10 天前
InputStage 责任链核心执行流程
android·android studio·android jetpack
传奇开心果编程11 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程12 天前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程12 天前
【现代声明式UI学与练】第9课 性能优化——渲染优化、列表优化、内存优化、启动优化
学习·flutter·react native·ui·性能优化·swiftui·android jetpack
天空之城--14 天前
Android一周动态:Android 18首次官宣、Compose Material3 1.4转正(5趋势+5资讯)
android·人工智能·flutter·架构·android jetpack