我们来看看SharedFlow的构造函数
public fun MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow
其主要有3个参数
1.replay表示当新的订阅者Collect时,发送几个已经发送过的数据给它,默认为0,即默认新订阅者不会获取以前的数据
2.extraBufferCapacity表示减去replay,MutableSharedFlow还缓存多少数据,默认为0
3.onBufferOverflow表示缓存策略,即缓冲区满了之后Flow如何处理,默认为挂起
简单使用如下:
//ViewModel
val sharedFlow=MutableSharedFlow()
viewModelScope.launch{
sharedFlow.emit(“Hello”)
sharedFlow.emit(“SharedFlow”)
}
//Activity
lifecycleScope.launch{
viewMode.sharedFlow.collect {
print(it)
}
}
3.3 将冷流转化为SharedFlow
 
普通flow可使用shareIn扩展方法,转化成SharedFlow
val sharedFlow by lazy {
flow {
//…
}.shareIn(viewModelScope, WhileSubscribed(500), 0)
}
shareIn主要也有三个参数:
started 接受以下的三个值:
1.Lazily: 当首个订阅者出现时开始,在scope指定的作用域被结束时终止。
2.Eagerly: 立即开始,而在scope指定的作用域被结束时终止。
3.WhileSubscribed: 这种情况有些复杂,后面会详细讲解
对于那些只执行一次的操作,您可以使用Lazily或者Eagerly。然而,如果您需要观察其他的流,就应该使用WhileSubscribed来实现细微但又重要的优化工作
3.4 Whilesubscribed策略
 
WhileSubscribed策略会在没有收集器的情况下取消上游数据流,通过shareIn运算符创建的SharedFlow会把数据暴露给视图 (View),同时也会观察来自其他层级或者是上游应用的数据流。
让这些流持续活跃可能会引起不必要的资源浪费,例如一直通过从数据库连接、硬件传感器中读取数据等等。当您的应用转而在后台运行时,您应当保持克制并中止这些协程。
public fun WhileSubscribed(
stopTimeoutMillis: Long = 0,
replayExpirationMillis: Long = Long.MAX_VALUE
)
如上所示,它支持两个参数:
-  1. stopTimeoutMillis控制一个以毫秒为单位的延迟值,指的是最后一个订阅者结束订阅与停止上游流的时间差。默认值是 0 (立即停止).这个值非常有用,因为您可能并不想因为视图有几秒钟不再监听就结束上游流。这种情况非常常见——比如当用户旋转设备时,原来的视图会先被销毁,然后数秒钟内重建。
-  2. replayExpirationMillis表示数据重播的过时时间,如果用户离开应用太久,此时您不想让用户看到陈旧的数据,你可以用到这个参数
4. StateFlow介绍
4.1 为什
 
么引入StateFlow
我们前面刚刚看了SharedFlow,为什么又冒出个StateFlow?
StateFlow 是 SharedFlow 的一个比较特殊的变种,StateFlow 与 LiveData 是最接近的,因为:
-  1.它始终是有值的。 
-  2.它的值是唯一的。 
-  3.它允许被多个观察者共用 (因此是共享的数据流)。 
-  4.它永远只会把最新的值重现给订阅者,这与活跃观察者的数量是无关的。 
可以看出,StateFlow与LiveData是比较接近的,可以获取当前的值,可以想像之所以引入StateFlow就是为了替换LiveData
总结如下:
1.StateFlow继承于SharedFlow,是SharedFlow的一个特殊变种
2.StateFlow与LiveData比较相近,相信之所以推出就是为了替换LiveData
4.2 StateFlow的简单使用
 
我们先来看看构造函数:
public fun MutableStateFlow(value: T): MutableStateFlow = StateFlowImpl(value ?: NULL)
1.StateFlow构造函数较为简单,只需要传入一个默认值
2.StateFlow本质上是一个replay为1,并且没有缓冲区的SharedFlow,因此第一次订阅时会先获得默认值
3.StateFlow仅在值已更新,并且值发生了变化时才会返回,即如果更新后的值没有变化,也没会回调Collect方法,这点与LiveData不同
与StateFlow类似,我们也可以用stateIn将普通流转化成SharedFlow
val result: StateFlow<Result> = someFlow
.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = Result.Loading
)
与shareIn类似,唯一不同的时需要传入一个默认值
同时之所以WhileSubscribed中传入了5000,是为了实现等待5秒后仍然没有订阅者存在就终止协程的功能,这个方法有以下功能
-  用户将您的应用转至后台运行,5 秒钟后所有来自其他层的数据更新会停止,这样可以节省电量。 
-  最新的数据仍然会被缓存,所以当用户切换回应用时,视图立即就可以得到数据进行渲染。 
-  订阅将被重启,新数据会填充进来,当数据可用时更新视图。 
-  在屏幕旋转时,因为重新订阅的时间在5s内,因此上游流不会中止 
4.3 在页面中观察StateFlow
 
与LiveData类似,我们也需要经常在页面中观察StateFlow
观察StateFlow需要在协程中,因此我们需要协程构建器,一般我们会使用下面几种
-  lifecycleScope.launch: 立即启动协程,并且在本Activity或Fragment销毁时结束协程。
-  LaunchWhenStarted和LaunchWhenResumed,它会在lifecycleOwner进入X状态之前一直等待,又在离开X状态时挂起协程

如上图所示:
1.使用launch是不安全的,在应用在后台时也会接收数据更新,可能会导致应用崩溃
2.使用launchWhenStarted或launchWhenResumed会好一些,在后台时不会接收数据更新,但是,上游数据流会在应用后台运行期间保持活跃,因此可能浪费一定的资源
这么说来,我们使用WhileSubscribed进行的配置岂不是无效了吗?订阅者一直存在,只有页面关闭时才会取消订阅
官方推荐repeatOnLifecycle来构建协程
在某个特定的状态满足时启动协程,并且在生命周期所有者退出该状态时停止协程,如下图所示。

比如在某个Fragment的代码中:
onCreateView(…) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(STARTED) {
myViewModel.myUiState.collect { … }
}
}
}
当这个Fragment处于STARTED状态时会开始收集流,并且在RESUMED状态时保持收集,最终在Fragment进入STOPPED状态时结束收集过程。
结合使用repeatOnLifecycle API和WhileSubscribed,可以帮助您的应用妥善利用设备资源的同时,发挥最佳性能
4.4 页面中观察Flow的最佳方式
 
通过ViewModel暴露数据,并在页面中获取的最佳方式是:
-  ✔️ 使用带超时参数的 WhileSubscribed策略暴露Flow。示例 1
-  ✔️ 使用 repeatOnLifecycle来收集数据更新。示例 2

最佳实践如上图所示,如果采用其他方式,上游数据流会被一直保持活跃,导致资源浪费
当然,如果您并不需要使用到Kotlin Flow的强大功能,就用LiveData好了 😃
5 StateFlow与SharedFlow有什么区别?
d策略暴露Flow`。示例 1
- ✔️ 使用 repeatOnLifecycle来收集数据更新。示例 2

最佳实践如上图所示,如果采用其他方式,上游数据流会被一直保持活跃,导致资源浪费
当然,如果您并不需要使用到Kotlin Flow的强大功能,就用LiveData好了 😃
5 StateFlow与SharedFlow有什么区别?



