当前位置:   article > 正文

Kotlin Flows 流 — 基础

Kotlin Flows 流 — 基础

Kotlin Flows 流 — 基础

上一篇研究了 Kotlin 协程。上一篇文章重点介绍了协程的一些基础知识,如协程上下文(CoroutineContext)、协程作用域(CoroutineScope)、协程构建器等。如承诺的那样,这是关于流(Flows)的后续文章。

什么是流?

可以异步计算的数据流被称为流(Flow)。Flow,像 LiveData 和 RxJava 流一样,允许你实现观察者模式:由一个对象(源)维护其依赖项列表,称为观察者(收集器),并在任何状态变化时自动通知它们。流使用挂起函数异步产生和消费值。

要创建流,首先你需要创建一个生产者:

  1. val randomFlow: Flow<Int> = flow {
  2.     repeat(10) { it ->
  3.         emit(it+1// 向流中发射请求的结果
  4.         delay(1000// 挂起协程1秒
  5.     }
  6. }

要收集流,首先你需要启动一个协程,因为流在底层是操作协程的。使用 collect 操作符来收集它发射的值。

  1. lifecycleScope.launch {
  2.     viewModel.uiStateFlow.collect { it ->
  3.         binding.uiText.text = it.toString()
  4.     }
  5. }

有两种不同类型的流:

冷流(Cold Flow)

它不会开始产生值,直到有人开始收集它们。它只能有一个订阅者,并且不存储数据。 // 常规流示例

  1. val coldFlow = flow {
  2.     emit(0
  3.     emit(1)
  4.     emit(2)
  5. }
  6. launch { // 第一次调用 collect
  7.     coldFlow.collect { value ->
  8.         println("cold flow collector 1 received: $value")
  9.     }
  10.     delay(2500)
  11.         
  12. // 第二次调用 collect
  13.     coldFlow.collect { value ->
  14.         println("cold flow collector 2 received: $value")
  15.     }
  16. }

// 结果 // 两个收集器都会从开始获取所有值。 // 对于两个收集器,相应的流都从开始启动。

  1. flow collector 1 received: [012]
  2. flow collector 1 received: [012]

热流(Hot Flow)

即使没有人收集它们,它也会产生值。它可以有多个订阅者,并且可以存储数据。 // SharedFlow 示例

  1. val sharedFlow = MutableSharedFlow<Int>()
  2. sharedFlow.emit(0)
  3. sharedFlow.emit(1)
  4. sharedFlow.emit(2)
  5. sharedFlow.emit(3)
  6. sharedFlow.emit(4)
  7. launch {
  8.     sharedFlow.collect { value ->
  9.         println("SharedFlow collector 1 received: $value")
  10.     }
  11.     delay(2500)
  12.         
  13. // 第二次调用 collect
  14.     sharedFlow.collect { value ->
  15.         println("SharedFlow collector 2 received: $value")
  16.     }
  17. }

// 结果 // 收集器将从它们开始收集的地方获取值。 // 这里,第1个收集器获取了所有值。但第2个收集器只获取了在2500毫秒后发射的值,因为它在2500毫秒后开始收集。

  1. SharedFlow collector 1 received: [0,1,2,3,4]
  2. SharedFlow collector 2 received: [2,3,4]

我们可以使用 stateIn()shareIn() 操作符分别将任何冷流转换为热流。

SharedFlow & StateFlow

StateFlow — StateFlow 是一个热流,代表一次只持有一个值的状态。它也是一个合流,意味着当新值被发射时,最近值被保留并立即发射给新的收集器。当你需要为状态维护一个单一的真实来源并自动用最新状态更新所有收集器时,它很有用。它始终有一个初始值,并且只存储最近发射的值。

  1. class HomeViewModel : ViewModel() {
  2.     private val _textStateFlow = MutableStateFlow("Hello World")
  3.     val stateFlow =_textStateFlow.asStateFlow()
  4.     fun triggerStateFlow(){
  5.         _textStateFlow.value="State flow"
  6.     }
  7. }
  8. // 在 Activity/Fragment 中收集 StateFlow
  9. class HomeFragment : Fragment() {
  10.     private val viewModel: HomeViewModel by viewModels()
  11.     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  12.         super.onViewCreated(view, savedInstanceState)
  13. lifecycleScope.launchWhenStarted {
  14.   // 触发流并开始监听值
  15.   // collectLatest() 是 Kotlin 的 Flow API 中的一个高阶函数
  16.   // 它允许你从流中收集发射的值,并仅对最近值执行转换。它类似于
  17.   // collect(), 后者用于收集所有发射的值,但 collectLatest 只处理最新发射的值并
  18.   // 忽略尚未处理的任何先前值。
  19.     viewModel.stateFlow.collectLatest {
  20.           binding.stateFlowButton.text = it
  21.     }
  22.   }
  23. }
  24. // 在 Compose 中收集 StateFlow
  25. @Compose
  26. fun HomeScreen() {
  27.  // Compose 提供了 collectAsStateWithLifecycle 函数,它
  28.  // 从流中收集值,并提供最新值以供使用
  29.  // 需要的地方。当新的流值被发射时,我们得到更新的
  30.  // 值,并且重新组合以更新值的状态。
  31.  // 它默认使用 LifeCycle.State.Started 在生命周期处于指定状态时开始收集值,并在其下降时停止。
  32.   val someFlow by viewModel.flow.collectAsStateWithLifecycle()
  33.   
  34. }

SharedFlow — SharedFlow 是一个热流,可以有多个收集器。它可以独立于收集器发射值,多个收集器可以从流中收集相同的值。当你需要向多个收集器广播值或当你想要有多个订阅者订阅相同的数据流时,它很有用。它没有初始值,你可以配置其重放缓存来为新收集器存储一定数量的先前发射的值。

  1. class HomeViewModel : ViewModel() {
  2.     private val _events = MutableSharedFlow<Event>() // 私有可变共享流
  3.     val events = _events.asSharedFlow() // 公开暴露为只读共享流
  4.     suspend fun produceEvent(event: Event) {
  5.         _events.emit(event) // 直到所有订阅者接收到它才挂起
  6.     }
  7. }
  8. // 在 Activity/Fragment 中收集 StateFlow
  9. class HomeFragment : Fragment() {
  10.     private val viewModel: HomeViewModel by viewModels()
  11.     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  12.         super.onViewCreated(view, savedInstanceState)
  13. lifecycleScope.launchWhenStarted {
  14.   // 触发流并开始监听值
  15.   // collectLatest() 是 Kotlin 的 Flow API 中的一个高阶函数
  16.   // 它允许你从流中收集发射的值,并仅对最近值执行转换。它类似于
  17.   // collect(), 后者用于收集所有发射的值,但 collectLatest 只处理最新发射的值并
  18.   // 忽略尚未处理的任何先前值。
  19.     viewModel.events.collectLatest {
  20.           binding.eventFlowButton.text = it
  21.     }
  22.   }
  23. }
  24. // 在 Compose 中收集 StateFlow
  25. @Compose
  26. fun HomeScreen() {
  27.  // Compose 提供了 collectAsStateWithLifecycle 函数,它
  28.  // 从流中收集值,并提供最新值以供使用
  29.  // 需要的地方。当新的流值被发射时,我们得到更新的
  30.  // 值,并且重新组合以更新值的状态。
  31.  // 它默认使用 LifeCycle.State.Started 在生命周期处于指定状态时开始收集值,并在其下降时停止。
  32.   val someFlow by viewModel.events.collectAsStateWithLifecycle()
  33.   
  34. }

流中的异常处理

Kotlin 流提供了几种处理异常和错误的机制。

try-catch 块 — 处理异常的基本方法之一是在流中使用 try-catch 块。

  1. flow {
  2.     try {
  3.         emit(productsService.fetchProducts())
  4.     } catch (e: Exception) {
  5.         emitError(e)
  6.     }
  7. }

catch 操作符 — Flow 中的 catch 操作符允许你通过将错误处理逻辑封装在一个地方来处理异常。

  1. flow {
  2.     emit(productsService.fetchProducts())
  3. }.catch { e ->
  4.     emitError(e)
  5. }

onCompletion 操作符 — 用于在流完成后执行代码,无论是成功完成还是异常完成。

  1. flow {
  2.     emit(productsService.fetchProducts())
  3. }.onCompletion { cause ->
  4.     if (cause != null) {
  5.         emitError(cause)
  6.     }
  7. }

自定义错误处理 — 在 Android 的复杂场景中,我们可以创建自定义操作符或扩展函数,以适合我们应用程序的方式处理错误。

  1. fun <T> Flow<T>.sampleErrorHandler(): Flow<Result<T>> = transform { value ->
  2.     try {
  3.         emit(Result.Success(value))
  4.     } catch (e: Exception) {
  5.         emit(Result.Error(e))
  6.     }
  7. }

Flow vs LiveData

LiveData 是生命周期感知的,这意味着它自动管理观察者的生命周期,确保仅在观察者处于活动状态时才传递更新。而流默认不是生命周期感知的。我们可以使用 Compose 中的 collectLatest() 或 collectAsStateWithLifecycle() 函数来从流中收集结果。

流提供更多的灵活性,适合更复杂的异步数据操作,而 LiveData 通常用于更简单的 UI 更新。

流提供内置的背压支持,允许控制数据发射和处理的速率,而 LiveData 不支持背压处理。

流提供丰富的操作符集合,用于顺序和结构化处理,而 LiveData 专注于将最新数据传递给观察者。

感谢阅读!如果你学到了新东西,请关注我获取更多

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/891438
推荐阅读
  

闽ICP备14008679号