StateFlow 中update的用法

在 Kotlin Coroutines 中,MutableStateFlow 是用于管理状态的核心 Hot Flow。更新状态主要有 value 赋值emitupdategetAndUpdateupdateAndGetcompareAndSet 等方法。它们的核心区别在于 并发安全性原子性返回值

一、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")
    }
}
相关推荐
阿巴斯甜7 小时前
ARouter的使用
android jetpack
ljt272496066120 小时前
Compose笔记(七十七)--视频录制
笔记·android jetpack
ljt27249606611 天前
Compose笔记(七十六)--拍照预览
笔记·android jetpack
alexhilton2 天前
Jetpack Compose元球边缘效果
android·kotlin·android jetpack
阿巴斯甜2 天前
Navigation中,怎么用这个app:launchSingleTop="true"
android jetpack
阿巴斯甜2 天前
Android 中Navigation的使用
android jetpack
simplepeng6 天前
再见 PredictiveBackHandler:如何迁移到 Compose 中的新导航事件
android jetpack
alexhilton6 天前
在Compose中用Shader实现透明的粘稠元球效果
android·kotlin·android jetpack
ljt27249606619 天前
Compose笔记(七十四)--BlurMaskFilter
笔记·android jetpack