当前位置: 首页 > news >正文

Kotlin Flow流

一 Kotlin Flow 中的 stateIn 和 shareIn

一、简单比喻理解

想象一个水龙头(数据源)和几个水杯(数据接收者):

  • 普通 Flow(冷流):每个水杯来接水时,都要重新打开水龙头从头放水
  • stateIn/shareIn(热流):水龙头一直开着,水存在一个水池里,任何水杯随时来接都能拿到水

二、stateIn 是什么?

就像手机的状态栏

  • 总是显示最新的一条信息(有当前值
  • 新用户打开手机时,立刻能看到最后一条消息
  • 适合用来表示"当前状态",比如:
    • 用户登录状态(已登录/未登录)
    • 页面加载状态(加载中/成功/失败)
    • 实时更新的数据(如股票价格)

代码示例:

// 创建一个永远知道当前温度的温度计
val currentTemperature = sensorFlow.stateIn(scope = viewModelScope,  // 在ViewModel生命周期内有效started = SharingStarted.WhileSubscribed(5000), // 5秒无订阅就暂停initialValue = 0 // 初始温度0度)// 在Activity中读取(总是能拿到当前温度)
textView.text = "${currentTemperature.value}°C"

三、shareIn 是什么?

就像广播电台

  • 不保存"当前值"(没有.value属性)
  • 新听众打开收音机时,可以选择:
    • 从最新的一条新闻开始听(replay=1)
    • 只听新新闻(replay=0)
  • 适合用来处理"事件",比如:
    • 显示Toast提示
    • 页面跳转指令
    • 一次性通知

代码示例:

// 创建一个消息广播站
val messages = notificationFlow.shareIn(scope = viewModelScope,started = SharingStarted.Lazily, // 有人收听时才启动replay = 1 // 新听众能听到最后1条消息)// 在Activity中收听广播
lifecycleScope.launch {messages.collect { msg ->Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()}
}

四、主要区别对比

特性stateIn (状态栏)shareIn (广播电台)
有无当前值有(.value 直接访问)无(必须通过collect接收)
新订阅者立即获得最新值可配置获得最近N条(replay)
典型用途持续更新的状态(如用户积分)一次性事件(如"购买成功"提示)
内存占用始终保存最新值按需缓存(可配置)
是否热流

五、为什么要用它们?

  1. 节省资源:避免重复计算(多个界面可以共享同一个数据源)

    • ❌ 不用时:每个界面都单独请求一次网络数据
    • ✅ 使用后:所有界面共享同一份网络数据
  2. 保持一致性:所有订阅者看到的数据完全相同

    • 比如用户头像更新后,所有界面立即同步
  3. 自动管理生命周期

    • 当Activity销毁时自动停止收集
    • 当配置变更(如屏幕旋转)时保持数据不丢失

六、生活场景类比

场景1:微信群(stateIn)

  • 群里最后一条消息就是当前状态(.value)
  • 新成员进群立刻能看到最后一条消息
  • 适合:工作群的状态同步

场景2:电台广播(shareIn)

  • 主播不断发送新消息
  • 听众打开收音机时:
    • 可以设置是否听之前的回放(replay)
    • 但无法直接问"刚才最后一首歌是什么"(无.value)
  • 适合:交通路况实时播报

七、什么时候用哪个?

用 stateIn 当:

  • 需要随时知道"当前值"
  • 数据会持续变化且需要被多个地方使用
  • 例如:
    • 用户登录状态
    • 购物车商品数量
    • 实时位置更新

用 shareIn 当:

  • 只关心新事件,不关心历史值
  • 事件可能被多个接收者处理
  • 例如:
    • "订单支付成功"通知
    • 错误提示消息
    • 页面跳转指令

八、超简单选择流程图

要管理持续变化的状态吗?是 → 需要直接访问当前值吗?是 → 用 stateIn否 → 用 shareIn(replay=1)否 → 这是一次性事件吗?是 → 用 shareIn(replay=0)

记住这个简单的口诀:
“状态用state,事件用share,想要回放加replay”

二 Kotlin Flow 的 shareInstateIn 操作符完全指南

在 Kotlin Flow 的使用中,shareInstateIn 是两个关键的操作符,用于优化流的共享和状态管理。本教程将深入解析这两个操作符的使用场景、区别和最佳实践。

一、核心概念解析

1. 冷流 vs 热流

  • 冷流 (Cold Flow):每个收集者都会触发独立的执行(如普通的 flow{} 构建器)
  • 热流 (Hot Flow):数据发射独立于收集者存在(如 StateFlowSharedFlow

2. 为什么需要 shareIn/stateIn?

  • 避免对上游冷流进行重复计算
  • 多个收集者共享同一个数据源
  • 将冷流转换为热流以提高效率

二、stateIn 操作符详解

基本用法

val sharedFlow: StateFlow<Int> = flow {// 模拟耗时操作emit(repository.fetchData())
}.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(),initialValue = 0
)

参数说明:

  • scope:共享流的协程作用域(通常用 viewModelScope
  • started:共享启动策略(后文详细讲解)
  • initialValue:必须提供的初始值

特点:

  1. 总是有当前值(通过 value 属性访问)
  2. 新收集者立即获得最新值
  3. 适合表示 UI 状态

使用场景示例:

用户个人信息状态管理

class UserViewModel : ViewModel() {private val _userState = repository.userUpdates() // Flow<User>.map { it.toUiState() }.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = UserState.Loading)val userState: StateFlow<UserState> = _userState
}

三、shareIn 操作符详解

基本用法

val sharedFlow: SharedFlow<Int> = flow {emit(repository.fetchData())
}.shareIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(),replay = 1
)

参数说明:

  • replay:新收集者接收的旧值数量
  • extraBufferCapacity:超出 replay 的缓冲大小
  • onBufferOverflow:缓冲策略(SUSPEND, DROP_OLDEST, DROP_LATEST

特点:

  1. 可以有多个订阅者
  2. 没有 value 属性,必须通过收集获取数据
  3. 适合事件处理(如 Toast、导航事件)

使用场景示例:

全局事件通知

class EventBus {private val _events = MutableSharedFlow<Event>()val events = _events.asSharedFlow()suspend fun postEvent(event: Event) {_events.emit(event)}// 使用 shareIn 转换外部流val externalEvents = someExternalFlow.shareIn(scope = CoroutineScope(Dispatchers.IO),started = SharingStarted.Eagerly,replay = 0)
}

四、started 参数深度解析

1. SharingStarted.Eagerly

  • 行为:立即启动,无视是否有收集者
  • 用例:需要预先缓存的数据
  • 风险:可能造成资源浪费
started = SharingStarted.Eagerly

2. SharingStarted.Lazily

  • 行为:在第一个收集者出现时启动,保持活跃直到 scope 结束
  • 用例:长期存在的共享数据
  • 注意:可能延迟首次数据获取
started = SharingStarted.Lazily

3. SharingStarted.WhileSubscribed()

  • 行为
    • 有收集者时活跃
    • 最后一个收集者消失后保持一段时间(默认 0ms)
    • 可配置 stopTimeoutMillisreplayExpirationMillis
  • 用例:大多数 UI 相关状态
// 保留5秒供可能的重新订阅
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000)

五、关键区别对比

特性stateInshareIn
返回类型StateFlowSharedFlow
初始值必须提供无要求
新收集者获取立即获得最新 value获取 replay 数量的旧值
值访问通过 .value 直接访问必须通过收集获取
典型用途UI 状态管理事件通知/数据广播
背压处理总是缓存最新值可配置缓冲策略

六、最佳实践指南

1. ViewModel 中的标准模式

class MyViewModel : ViewModel() {// 状态管理用 stateInval uiState = repository.data.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = null)// 事件处理用 shareInval events = repository.events.shareIn(scope = viewModelScope,started = SharingStarted.Lazily,replay = 1)
}

2. 合理选择 started 策略

  • UI 状态WhileSubscribed(stopTimeoutMillis = 5000)
  • 配置变更需保留Lazily
  • 全局常驻数据Eagerly

3. 避免常见错误

错误1:在每次调用时创建新流

// 错误!每次调用都创建新流
fun getUser() = repository.getUserFlow().stateIn(viewModelScope, SharingStarted.Eagerly, null)// 正确:共享同一个流
private val _user = repository.getUserFlow().stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val user: StateFlow<User?> = _user

错误2:忽略 replay 配置

// 可能丢失事件
.shareIn(scope, SharingStarted.Lazily, replay = 0)// 更安全的配置
.shareIn(scope, SharingStarted.Lazily, replay = 1)

七、高级应用场景

1. 结合 Room 数据库

@Dao
interface UserDao {@Query("SELECT * FROM user")fun observeUsers(): Flow<List<User>>
}// ViewModel 中
val users = userDao.observeUsers().stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(),initialValue = emptyList())

2. 实现自动刷新功能

val autoRefreshData = flow {while(true) {emit(repository.fetchLatest())delay(30_000) // 每30秒刷新}
}.shareIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(),replay = 1
)

3. 多源数据合并

val combinedData = combine(repo1.data.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1),repo2.data.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
) { data1, data2 ->data1 + data2
}.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(),initialValue = emptyList()
)

八、性能优化技巧

  1. 合理设置 replay

    • UI 状态:replay = 1(确保新订阅者立即获得状态)
    • 事件通知:replay = 0(避免重复处理旧事件)
  2. 使用 WhileSubscribed 的过期策略

    started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000,replayExpirationMillis = 60_000 // 1分钟后丢弃缓存
    )
    
  3. 避免过度缓冲

    .shareIn(scope = ...,replay = 1,extraBufferCapacity = 1, // 总共缓冲2个值onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    

九、测试策略

1. 测试 StateFlow

@Test
fun testStateFlow() = runTest {val testScope = TestScope()val flow = flowOf(1, 2, 3)val stateFlow = flow.stateIn(scope = testScope,started = SharingStarted.Eagerly,initialValue = 0)assertEquals(0, stateFlow.value) // 初始值testScope.advanceUntilIdle()assertEquals(3, stateFlow.value) // 最后发射的值
}

2. 测试 SharedFlow

@Test
fun testSharedFlow() = runTest {val testScope = TestScope()val flow = flowOf("A", "B", "C")val sharedFlow = flow.shareIn(scope = testScope,started = SharingStarted.Eagerly,replay = 1)val results = mutableListOf<String>()val job = launch {sharedFlow.collect { results.add(it) }}testScope.advanceUntilIdle()assertEquals(listOf("A", "B", "C"), results)job.cancel()
}

十、总结决策树

何时使用 stateIn

  • 需要表示当前状态(有 .value 属性)
  • UI 需要立即访问最新值
  • 适合:页面状态、表单数据、加载状态

何时使用 shareIn

  • 处理一次性事件
  • 需要自定义缓冲策略
  • 适合:Toast 消息、导航事件、广播通知

选择哪种 started 策略?

  • WhileSubscribed():大多数 UI 场景
  • Lazily:配置变更需保留数据
  • Eagerly:需要预加载的全局数据

通过本教程,应该已经掌握了 shareInstateIn 的核心用法和高级技巧。正确使用这两个操作符可以显著提升应用的性能和资源利用率。

三 从 LiveData 迁移到 Kotlin Flow 完整教程

LiveData 长期以来是 Android 架构组件中状态管理的核心,但随着 Kotlin Flow 的成熟,Google 官方推荐将现有 LiveData 迁移到 Flow。本教程基于官方文章并扩展实践细节,完成平滑迁移。

一、为什么要从 LiveData 迁移到 Flow?

LiveData 的局限性

  1. 有限的运算符:只有简单的 map/switchMap 转换
  2. 线程限制:只能在主线程观察
  3. 一次性操作:不适合处理事件流
  4. 生命周期耦合:虽然方便但也限制了灵活性

Flow 的优势

  1. 丰富的操作符:filter, transform, combine 等 100+ 操作符
  2. 灵活的线程控制:通过 Dispatchers 指定执行线程
  3. 响应式编程:完整的事件流处理能力
  4. 协程集成:与 Kotlin 协程完美配合

二、基础迁移方案

1. 简单替换:LiveData → StateFlow

原 LiveData 代码

class MyViewModel : ViewModel() {private val _userName = MutableLiveData("")val userName: LiveData<String> = _userNamefun updateName(name: String) {_userName.value = name}
}

迁移为 StateFlow

class MyViewModel : ViewModel() {private val _userName = MutableStateFlow("")val userName: StateFlow<String> = _userName.asStateFlow()fun updateName(name: String) {_userName.value = name}
}

2. 在 UI 层收集 Flow

Activity/Fragment 中收集

lifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.userName.collect { name ->binding.textView.text = name}}
}

关键点:使用 repeatOnLifecycle 确保只在 UI 可见时收集,避免资源浪费

三、高级迁移模式

1. 数据流转换(替代 Transformations)

LiveData 方式

val userName: LiveData<String> = Transformations.map(_userName) { "Hello, $it!" 
}

Flow 方式

val userName: Flow<String> = _userName.map { "Hello, $it!" 
}

2. 多数据源合并(替代 MediatorLiveData)

LiveData 方式

val result = MediatorLiveData<String>().apply {addSource(liveData1) { value = "$it + ${liveData2.value}" }addSource(liveData2) { value = "${liveData1.value} + $it" }
}

Flow 方式

val result = combine(flow1, flow2) { data1, data2 ->"$data1 + $data2"
}

四、处理一次性事件(替代 SingleLiveEvent)

LiveData 常被滥用处理一次性事件(如 Toast、导航),Flow 提供了更专业的解决方案:

使用 SharedFlow

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

UI 层收集

lifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.events.collect { event ->when (event) {is EventViewModel.Event.ShowToast -> Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()EventViewModel.Event.NavigateToNext -> startActivity(Intent(this, NextActivity::class.java))}}}
}

五、Room 数据库迁移

LiveData 查询:

@Dao
interface UserDao {@Query("SELECT * FROM user")fun getUsers(): LiveData<List<User>>
}

迁移到 Flow:

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

ViewModel 中使用

val users: StateFlow<List<User>> = userDao.getUsers().stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = emptyList())

六、兼容现有代码的渐进式迁移

1. 使用 asLiveData() 临时兼容

val userFlow: Flow<User> = repository.getUserFlow()// 临时保持 LiveData 接口
val userLiveData: LiveData<User> = userFlow.asLiveData()

2. 混合使用策略

class HybridViewModel : ViewModel() {// 新功能使用 Flowprivate val _newFeatureState = MutableStateFlow("")val newFeatureState: StateFlow<String> = _newFeatureState.asStateFlow()// 旧功能暂保持 LiveDataprivate val _oldFeatureData = MutableLiveData(0)val oldFeatureData: LiveData<Int> = _oldFeatureData
}

七、测试策略调整

LiveData 测试:

@Test
fun testLiveData() {val liveData = MutableLiveData("test")assertEquals("test", liveData.value)
}

Flow 测试:

@Test
fun testFlow() = runTest {val flow = MutableStateFlow("test")val results = mutableListOf<String>()val job = launch {flow.collect { results.add(it) }}flow.value = "new value"assertEquals(listOf("test", "new value"), results)job.cancel()
}

八、性能优化技巧

  1. 使用 stateIn 共享流

    val sharedFlow = someFlow.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = null)
    
  2. 避免重复创建 Flow

    // 错误方式 - 每次调用都创建新流
    fun getUser() = userDao.getUserFlow()// 正确方式 - 共享同一个流
    private val _userFlow = userDao.getUserFlow()
    val userFlow = _userFlow
    
  3. 合理选择背压策略

    // 缓冲最新值
    val events = MutableSharedFlow<Event>(replay = 0,extraBufferCapacity = 1,onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    

九、常见问题解决方案

Q1: 如何确保 Flow 收集不会泄漏?

lifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {// 只有在此块内会激活收集viewModel.data.collect { ... }}
}

Q2: 为什么我的 Flow 不发射数据?

检查:

  1. Flow 是否被正确触发(冷流需要收集才会开始)
  2. 是否在正确的协程作用域内收集
  3. 是否有异常导致流终止

Q3: 如何处理 Java 代码调用?

// 暴露 LiveData 给 Java 模块
val javaCompatData: LiveData<String> = flow.asLiveData()

十、完整迁移示例

迁移前 ViewModel

class OldViewModel : ViewModel() {private val _data = MutableLiveData("")val data: LiveData<String> = _dataprivate val _event = SingleLiveEvent<Event>()val event: LiveData<Event> = _eventfun loadData() {viewModelScope.launch {_data.value = repository.fetchData()_event.value = Event.ShowToast("Loaded")}}
}

迁移后 ViewModel

class NewViewModel : ViewModel() {private val _data = MutableStateFlow("")val data: StateFlow<String> = _data.asStateFlow()private val _event = MutableSharedFlow<Event>()val event: SharedFlow<Event> = _event.asSharedFlow()fun loadData() {viewModelScope.launch {_data.value = repository.fetchData()_event.emit(Event.ShowToast("Loaded"))}}
}

通过本教程,可以系统性地将现有 LiveData 代码迁移到 Kotlin Flow。建议采用渐进式迁移策略,优先在新功能中使用 Flow,逐步改造旧代码。

四 Android 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() {// 私有可变的StateFlowprivate val _uiState = MutableStateFlow<UiState>(UiState.Loading)// 公开不可变的StateFlowval 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.Loadingtry {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/ErrorassertTrue(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.Loadingtry {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@launchval 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 应用的强大工具,结合协程可以提供更简洁、更安全的状态管理方案。

相关文章:

  • VulnHub-DC-2靶机
  • 父子组件双向绑定
  • 【单片机数码管实现第一位开始走0~9,1s后第二位再开始亮】2022-5-2
  • C++将整数换成分数 2024年信息素养大赛复赛 C++小学/初中组 算法创意实践挑战赛 真题详细解析
  • React useCallback函数
  • Oracle-ACL配置
  • “淘宝闪购”提前4天全量,意味着什么?
  • 使用PyTorch实现线性回归:从零实现到高级API
  • Python-pandas-操作Excel文件(读取数据/写入数据)及Excel表格列名操作详细分享
  • 轻量级在线Excel预览工具
  • [面试]SoC验证工程师面试常见问题(四)
  • 【多云PaaS】跨云平台的无缝迁移方案
  • 【中间件】brpc_基础_butex.h
  • FastAPI中的复杂查询与原子更新指南
  • 【Linux】Petalinux U-Boot
  • 【中间件】brpc_基础_bthread头文件
  • 精益数据分析(36/126):SaaS商业模式的指标动态与实践案例
  • 数据分析_问题/优化
  • 力扣838.推多米诺随笔
  • 变转速振动信号分析处理与故障诊断算法模块
  • 青海大学常务副校长(正厅级)任延明已任省卫健委党组书记
  • 5名中国公民在美国交通事故中遇难
  • 跳水世界杯总决赛:程子龙/朱子锋夺男子双人10米台冠军
  • 人民日报:上海“模速空间”何以汇聚超百家大模型企业
  • 澎湃回声丨23岁小伙“被精神病8年”续:今日将被移出“重精”管理系统
  • 海尔智家一季度营收791亿元:净利润增长15%,海外市场收入增超12%