kotlin 01flow-StateFlow 完整教程

一 Android StateFlow 完整教程:从入门到实战

StateFlow 是 Kotlin 协程库中用于状态管理的响应式流,特别适合在 Android 应用开发中管理 UI 状态。本教程将带全面了解 StateFlow 的使用方法。

1. StateFlow 基础概念

1.1 什么是 StateFlow?

StateFlow 是 Kotlin 协程提供的一种热流(Hot Flow),它具有以下特点:

  • 总是有当前值(初始值必须提供)
  • 只保留最新值
  • 支持多个观察者
  • 与 LiveData 类似但基于协程

1.2 StateFlow vs LiveData

特性StateFlowLiveData
生命周期感知否(需配合 lifecycleScope)
需要初始值
基于协程观察者模式
线程控制通过 Dispatcher主线程
背压处理自动处理自动处理

2. 基本使用

2.1 添加依赖

在 build.gradle 中添加:

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"
}

2.2 创建 StateFlow

class MyViewModel : ViewModel() {
    // 私有可变的StateFlow
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    
    // 公开不可变的StateFlow
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    sealed class UiState {
        object Loading : UiState()
        data class Success(val data: String) : UiState()
        data class Error(val message: String) : UiState()
    }
    
    fun loadData() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val result = repository.fetchData()
                _uiState.value = UiState.Success(result)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

2.3 在 Activity/Fragment 中收集 StateFlow

class MyActivity : AppCompatActivity() {
    private val viewModel: MyViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    when (state) {
                        is MyViewModel.UiState.Loading -> showLoading()
                        is MyViewModel.UiState.Success -> showData(state.data)
                        is MyViewModel.UiState.Error -> showError(state.message)
                    }
                }
            }
        }
    }
    
    private fun showLoading() { /*...*/ }
    private fun showData(data: String) { /*...*/ }
    private fun showError(message: String) { /*...*/ }
}

3. 高级用法

3.1 结合 SharedFlow 处理一次性事件

class EventViewModel : ViewModel() {
    private val _events = MutableSharedFlow<Event>()
    val events = _events.asSharedFlow()
    
    sealed class Event {
        data class ShowToast(val message: String) : Event()
        object NavigateToNextScreen : Event()
    }
    
    fun triggerEvent() {
        viewModelScope.launch {
            _events.emit(Event.ShowToast("Hello World!"))
        }
    }
}

// 在Activity中收集
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.events.collect { event ->
            when (event) {
                is EventViewModel.Event.ShowToast -> showToast(event.message)
                EventViewModel.Event.NavigateToNextScreen -> navigateToNext()
            }
        }
    }
}

3.2 状态合并 (combine)

val userName = MutableStateFlow("")
val userAge = MutableStateFlow(0)

val userInfo = combine(userName, userAge) { name, age ->
    "Name: $name, Age: $age"
}

// 收集合并后的流
userInfo.collect { info ->
    println(info)
}

3.3 状态转换 (map, filter, etc.)

val numbers = MutableStateFlow(0)

val evenNumbers = numbers
    .filter { it % 2 == 0 }
    .map { "Even: $it" }

evenNumbers.collect { println(it) }

4. 性能优化

4.1 使用 stateIn 缓存 StateFlow

val networkFlow = flow {
    // 模拟网络请求
    emit(repository.fetchData())
}

val cachedState = networkFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5000), // 5秒无订阅者停止
    initialValue = "Loading..."
)

4.2 避免重复收集

// 错误方式 - 每次重组都会创建新的收集器
@Composable
fun MyComposable(viewModel: MyViewModel) {
    val state by viewModel.state.collectAsState()
    // ...
}

// 正确方式 - 使用 derivedStateOf 或 remember
@Composable
fun MyComposable(viewModel: MyViewModel) {
    val state by remember { viewModel.state }.collectAsState()
    // ...
}

5. 测试 StateFlow

5.1 单元测试

@Test
fun `test state flow`() = runTest {
    val viewModel = MyViewModel()
    
    val results = mutableListOf<MyViewModel.UiState>()
    val job = launch {
        viewModel.uiState.collect { results.add(it) }
    }
    
    viewModel.loadData()
    advanceUntilIdle()
    
    assertEquals(3, results.size) // Loading, Success/Error
    assertTrue(results[0] is MyViewModel.UiState.Loading)
    
    job.cancel()
}

5.2 使用 Turbine 测试库

dependencies {
    testImplementation "app.cash.turbine:turbine:0.12.1"
}

@Test
fun `test with turbine`() = runTest {
    val viewModel = MyViewModel()
    
    viewModel.uiState.test {
        viewModel.loadData()
        
        assertEquals(MyViewModel.UiState.Loading, awaitItem())
        val success = awaitItem()
        assertTrue(success is MyViewModel.UiState.Success)
        
        cancelAndIgnoreRemainingEvents()
    }
}

6. 常见问题解答

Q1: StateFlow 和 LiveData 哪个更好?

StateFlow 更适合协程环境,LiveData 更简单但功能较少。新项目推荐 StateFlow。

Q2: 如何处理背压(Backpressure)?

StateFlow 自动处理背压,只保留最新值。

Q3: 为什么我的收集器没有收到更新?

检查:

  1. 是否在正确的生命周期范围内收集
  2. Flow 是否有发射新值
  3. 是否在正确的协程上下文中

Q4: 如何避免内存泄漏?

使用 repeatOnLifecycleflowWithLifecycle 确保只在活跃生命周期收集。

7. 完整示例项目

以下是一个完整的 ViewModel 示例:

class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
    private val _userState = MutableStateFlow<UserState>(UserState.Loading)
    val userState: StateFlow<UserState> = _userState.asStateFlow()
    
    private val _events = MutableSharedFlow<UserEvent>()
    val events: SharedFlow<UserEvent> = _events.asSharedFlow()
    
    init {
        loadUser()
    }
    
    fun loadUser() {
        viewModelScope.launch {
            _userState.value = UserState.Loading
            try {
                val user = userRepository.getUser()
                _userState.value = UserState.Success(user)
            } catch (e: Exception) {
                _userState.value = UserState.Error(e.message ?: "Unknown error")
                _events.emit(UserEvent.ShowErrorToast("Failed to load user"))
            }
        }
    }
    
    fun updateUserName(name: String) {
        viewModelScope.launch {
            val currentUser = (_userState.value as? UserState.Success)?.user ?: return@launch
            val updatedUser = currentUser.copy(name = name)
            _userState.value = UserState.Success(updatedUser)
            userRepository.updateUser(updatedUser)
        }
    }
    
    sealed class UserState {
        object Loading : UserState()
        data class Success(val user: User) : UserState()
        data class Error(val message: String) : UserState()
    }
    
    sealed class UserEvent {
        data class ShowErrorToast(val message: String) : UserEvent()
    }
}

通过本教程,应该已经掌握了 StateFlow 的核心用法。StateFlow 是构建响应式 Android 应用的强大工具,结合协程可以提供更简洁、更安全的状态管理方案。

二 StateFlow 在企业级 Android 开发中的典型用法:

1. UI 状态管理

class ProfileViewModel : ViewModel() {
    // UI状态封装
    data class UiState(
        val userName: String = "",
        val isLoading: Boolean = false,
        val error: String? = null  
    )

    private val _uiState = MutableStateFlow(UiState())
    val uiState = _uiState.asStateFlow()

    fun loadProfile() {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true) }
            try {
                val profile = repository.getProfile()
                _uiState.update { 
                    it.copy(
                        userName = profile.name,
                        isLoading = false
                    )
                }
            } catch(e: Exception) {
                _uiState.update { 
                    it.copy(
                        error = e.message,
                        isLoading = false
                    )
                }
            }
        }
    }
}

2. 多数据源合并

class WeatherViewModel : ViewModel() {
    private val _temperature = MutableStateFlow(20)
    private val _humidity = MutableStateFlow(50)
    
    val weatherState = combine(
        _temperature,
        _humidity
    ) { temp, humidity -> 
        WeatherState(temp, humidity)
    }.stateIn(
        viewModelScope,
        SharingStarted.WhileSubscribed(5000),
        WeatherState()  
    )
}

3. 事件处理

class LoginViewModel : ViewModel() {
    private val _events = MutableSharedFlow<LoginEvent>()
    val events = _events.asSharedFlow()

    fun login(username: String, password: String) {
        viewModelScope.launch {
            try {
                repository.login(username, password)
                _events.emit(LoginEvent.Success)
            } catch(e: Exception) {
                _events.emit(LoginEvent.Error(e.message))
            }
        }
    }
}

4. 数据缓存和防抖

class SearchViewModel : ViewModel() {
    private val _searchQuery = MutableStateFlow("")
    
    val searchResults = _searchQuery
        .debounce(300)
        .distinctUntilChanged()
        .flatMapLatest { query ->
            repository.search(query)
        }
        .stateIn(
            viewModelScope,
            SharingStarted.WhileSubscribed(5000),
            emptyList()
        )

    fun onQueryChanged(query: String) {
        _searchQuery.value = query
    }
}

5. 全局状态管理

object AppState {
    private val _isDarkMode = MutableStateFlow(false)
    val isDarkMode = _isDarkMode.asStateFlow()

    private val _language = MutableStateFlow(Locale.getDefault())
    val language = _language.asStateFlow()

    fun toggleDarkMode() {
        _isDarkMode.value = !_isDarkMode.value
    }

    fun setLanguage(locale: Locale) {
        _language.value = locale
    }
}

6. Room 数据库集成

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun observeUsers(): Flow<List<User>>
}

class UserRepository @Inject constructor(
    private val userDao: UserDao
) {
    val users: StateFlow<List<User>> = userDao
        .observeUsers()
        .stateIn(
            scope = CoroutineScope(Dispatchers.IO),
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )
}

7. 状态恢复

class SavedStateViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    
    private val _counter = savedStateHandle.getStateFlow("counter", 0)
    val counter = _counter.asStateFlow()

    fun increment() {
        _counter.value++  // 自动保存到 SavedStateHandle
    }
}

8. 网络状态监听

class NetworkViewModel @Inject constructor(
    connectivityManager: ConnectivityManager
) : ViewModel() {

    private val _networkState = MutableStateFlow<NetworkState>(NetworkState.Loading)
    val networkState = _networkState.asStateFlow()

    init {
        connectivityManager.registerNetworkCallback(
            NetworkRequest.Builder().build(),
            object : NetworkCallback() {
                override fun onAvailable(network: Network) {
                    _networkState.value = NetworkState.Connected
                }

                override fun onLost(network: Network) {
                    _networkState.value = NetworkState.Disconnected
                }
            }
        )
    }
}

这些用法覆盖了企业开发中的大部分场景,主要特点是:

  1. 状态管理清晰
  2. 生命周期安全
  3. 线程安全
  4. 支持状态恢复
  5. 易于测试
  6. 可组合性强
  7. 与其他 Jetpack 组件良好集成

建议在实际项目中根据具体需求选择合适的使用方式。

三 stateFlow和sharedFlow的区别

关于 StateFlowSharedFlow 的问题非常关键。它们都是 Kotlin Flow 的“热流”(hot stream)变种,广泛用于 事件驱动编程状态管理。下面逐条详细解答问题:


✅ 1. StateFlow 和 SharedFlow 的区别?能否互相转换?

特性StateFlowSharedFlow
是否需要初始值✅ 是(必须有)❌ 否(可选)
是否有当前值 .value✅ 是❌ 没有
是否支持粘性订阅(可立即获取最新值)✅ 是(始终有当前值)❌ 否(默认非粘性,取决于 replay)
用于什么状态管理(UI 状态)一次性事件(导航、吐司等)
是否自动重发✅ 是:新订阅者立即拿到当前值❌ 否(需设置 replay > 0 才能重发)
是否支持多次发送相同值❌ 否(相同值不会触发 collect)✅ 是
是否丢失事件❌ 不会丢(始终保留一个最新值)✅ 可能丢(如果没有设置 replay 或 buffer)

🔁 能否互相转换?

不能直接转换,因为它们底层设计不同。但可以人为地模拟:

val sharedFlow = MutableSharedFlow<Int>(replay = 1)
val stateFlow = sharedFlow.stateIn(scope, SharingStarted.Eagerly, initialValue)

或者从 StateFlow 获取 SharedFlow 的行为:

val stateFlow: StateFlow<Int> = MutableStateFlow(0)
val sharedFlow = stateFlow.shareIn(scope, SharingStarted.Lazily)

✅ 2. 是不是 StateFlow 就是 SharedFlow 多了 .value 和初始值?

可以这么说,但这只是表面。它们的语义不同:

  • StateFlow 表示的是状态的容器(如 ViewModel 中的 UI 状态)。
  • SharedFlow 更像是事件的通道,没有“当前状态”的概念。
// StateFlow 的特点
println(stateFlow.value) // 始终可以拿到当前值

// SharedFlow 没有 value,只能 collect
sharedFlow.collect { event -> ... }

✅ 3. 如何处理“被压的”问题(Backpressure、丢失事件)?

📌 StateFlow

  • 没有丢失问题:永远保留最后一个值。
  • 新订阅者会立刻收到当前值。
  • 不支持 backpressure,因为没有队列:只有一个值。
val sf = MutableStateFlow(0)
sf.value = 1
sf.value = 2
// collect 会立即收到 2(中间的1不会被保留)

📌 SharedFlow

  • 默认不保留任何事件replay = 0,无 buffer)。
  • 如果事件很快发送但没人 collect,就直接丢失
  • 可以设置 replayextraBufferCapacity 来控制:
val shared = MutableSharedFlow<Int>(
    replay = 2,
    extraBufferCapacity = 5
)
  • 设置 onBufferOverflow = BufferOverflow.DROP_OLDEST/ DROP_LATEST / SUSPEND 控制丢弃策略。

✅ 使用建议总结

需求推荐使用
需要保存并暴露当前状态(如 UI 状态)StateFlow
需要发送一次性事件,如 Toast、导航SharedFlow
想在订阅时立即拿到最后值StateFlow
想发送多个相同值SharedFlow(StateFlow 会去重)

四 MutableStateFlow 是如何更新值的?

_seatLevel.value = newValue_seatLevel.update { ... } 都可以用来更新 MutableStateFlow 的值,但它们在 并发安全性使用场景 上有区别:


1. _seatLevel.value = newValue(直接赋值)

  • 作用:直接设置 MutableStateFlow 的新值。
  • 特点
    • 非原子操作:如果在多线程环境下,直接赋值可能会导致竞态条件(Race Condition),因为 value 的读写不是线程安全的。
    • 简单直接:适用于单线程或确定不会并发更新的场景。
  • 示例
    _seatLevel.value = 5 // 直接设置新值
    

2. _seatLevel.update { ... }(原子更新)

  • 作用:以 原子方式 计算并更新 MutableStateFlow 的值。
  • 特点
    • 原子操作update 是线程安全的,内部使用 CAS (Compare-And-Swap) 机制,确保在并发环境下不会出现数据竞争。
    • 基于当前值计算:可以访问当前值(current),并返回新值。
    • 适合条件更新:适用于需要依赖当前值进行计算的场景(如递增、条件过滤等)。
  • 示例
    _seatLevel.update { current ->
        if (newLevel in 0..100) newLevel else current
    }
    
    这里:
    • current 是当前 _seatLevel 的值。
    • 返回的值会作为新值(如果 newLevel 不在 0..100 范围内,则保持原值)。

关键区别

特性_seatLevel.value = newValue_seatLevel.update { ... }
线程安全❌ 非原子操作,可能竞态条件✅ 原子操作,线程安全
是否依赖当前值❌ 直接赋值,不关心当前值✅ 可以访问当前值并计算新值
适用场景单线程或确定无并发更新多线程环境或需要条件更新

如何选择?

  1. 如果只是简单赋值,且确定不会并发更新(例如 UI 线程中),可以直接用 value = newValue
  2. 如果涉及多线程,或需要基于当前值计算新值(如递增、条件过滤等),应该用 update { ... }

底层实现

  • update 内部使用了 while (true) + compareAndSet 机制,确保原子性:
    public inline fun update(function: (T) -> T) {
        while (true) {
            val prevValue = value
            val nextValue = function(prevValue)
            if (compareAndSet(prevValue, nextValue)) {
                return
            }
        }
    }
    
    它会不断尝试更新,直到成功(避免并发冲突)。

总结

  • value = newValue:简单直接,但不适合并发场景。
  • update { ... }:线程安全,适合多线程或需要依赖当前值的更新。

在你的 updateLevel 函数中,由于涉及条件判断(newLevel in 0..100),使用 update 是更安全、更合理的选择。

四 为什么update 更新是原子性?

相关代码

public inline fun update(function: (T) -> T) {
    while (true) {
        val prevValue = value
        val nextValue = function(prevValue)
        if (compareAndSet(prevValue, nextValue)) {
            return
        }
    }
} 

这段代码之所以能保证 MutableStateFlow 的更新是 原子性(Atomic) 的,关键在于它使用了 CAS(Compare-And-Swap) 机制,并通过 while (true) 循环确保最终成功更新。我们逐行分析其原子性保证:


1. compareAndSet(prevValue, nextValue) 是关键

compareAndSetAtomic 操作(底层基于 CPU 的 CAS 指令),它的行为是:

fun compareAndSet(expect: T, newValue: T): Boolean
  • 如果当前 value == expect(说明其他线程尚未修改),则更新为 newValue 并返回 true
  • 如果当前 value != expect(说明其他线程已经修改),则放弃更新并返回 false

由于 compareAndSet硬件级原子操作,它保证了:

  • 读取 value比较 expect写入 newValue 这三个步骤是 不可分割的(不会被其他线程打断)。

2. while (true) 循环的作用

如果 compareAndSet 失败(返回 false),说明其他线程已经修改了 value,此时:

  1. 重新读取最新的 valueval prevValue = value)。
  2. 重新计算 nextValuefunction(prevValue))。
  3. 再次尝试 compareAndSet,直到成功为止。

这种 乐观锁(Optimistic Locking) 策略确保了:

  • 即使多线程竞争,最终所有更新都会 按顺序应用(不会丢失任何修改)。
  • 不会发生 死锁(因为没有阻塞,只是重试)。

3. 为什么能保证原子性?

  • compareAndSet 是原子的:单次 compareAndSet 调用是线程安全的。
  • 循环直到成功:即使其他线程并发修改,当前线程最终会基于最新的值成功更新。
  • 函数式更新function(prevValue) 的计算是基于最新的 prevValue,不会出现脏数据。

4. 对比非原子更新的问题

如果直接用 value = newValue

// 非原子操作,可能导致竞态条件
_seatLevel.value = _seatLevel.value + 1

在多线程环境下:

  1. 线程 A 读取 value = 0
  2. 线程 B 读取 value = 0
  3. 线程 A 写入 value = 1
  4. 线程 B 写入 value = 1(本应是 2,但结果错误!)。

update 能避免这个问题:

_seatLevel.update { current -> current + 1 } // 线程安全

5. 原子性的直观体现

从代码中可以看出原子性的关键点:

  1. val prevValue = value:读取当前值(可能被其他线程修改)。
  2. val nextValue = function(prevValue):基于当前值计算新值。
  3. if (compareAndSet(prevValue, nextValue))
    • 只有 value 仍等于 prevValue 时才会更新。
    • 如果失败(说明其他线程已修改),则 重试,确保最终更新是基于最新值。

6. 类似机制的对比

  • Java 的 AtomicInteger
    atomicInt.updateAndGet(x -> x + 1); // 同样基于 CAS
    
  • 数据库的乐观锁
    UPDATE table SET value = newValue WHERE value = oldValue; -- 类似 CAS
    

总结

这段代码的原子性由以下两点保证:

  1. compareAndSet 的原子性:确保比较和更新的操作不可分割。
  2. 循环重试机制:确保并发冲突时最终能成功更新。

因此,update { ... } 是线程安全的,适合在多线程环境下使用,而直接 value = newValue 不是。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值