StateFlow 中update的用法

26 阅读3分钟

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

一、update 函数详解

作用:基于旧值计算新值,并通过 CAS (Compare-And-Swap)  机制保证原子性线程安全,彻底解决竞态条件 (Race Condition)

用法

// 1. 基本数据类型(计数)
_counter.update { prevCount -> prevCount + 1 }

// 2. 数据类(更新状态对象,必须使用.copy())
_uiState.update { currentState ->
        currentState.copy(
                isLoading = false,
                data = newData
        )
}

底层原理(伪代码)

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

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

  3. value 仅用于赋值value 属性主要用于直接读取当前值无条件覆盖

五、代码示例

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")
    }
}