Kotlin StateFlow、SharedFlow、Channel解析

StateFlowSharedFlow 是 Flow API,允许数据流以最优方式发出状态更新并向多个使用方发出值。

StateFlow

官方文档解释:StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新。还可通过其 value 属性读取当前状态值。如需更新状态并将其发送到数据流,请为 MutableStateFlow 类的 value 属性分配一个新值。

StateFlow有两种类型: StateFlow 和 MutableStateFlow :

public interface StateFlow<out T> : SharedFlow<T> {
    public val value: T
}

public interface MutableStateFlow<T> : StateFlow<T>, MutableSharedFlow<T> {
    public override var value: T

    public fun compareAndSet(expect: T, update: T): Boolean
}

状态由其值表示。任何对值的更新都会反馈新值到所有流的接收器中。

StateFlow 基本使用
class StateFlow {

    private val _state = MutableStateFlow<String>("unKnown")
    val state: kotlinx.coroutines.flow.StateFlow<String> get() = _state

    fun getApi(scope: CoroutineScope) {
        scope.launch {
            var res = getApi()
            _state.value = res
        }
    }

    /**
     * 进行网络Api请求
     */
    private suspend fun getApi() = withContext(Dispatchers.IO) {
        delay(2000) //模拟耗时请求
        "result"
    }

}

    private fun stateFlowTest() = runBlocking {
        val stateFlow = StateFlow()
        stateFlow.getApi(this) //开始获取结果

        launch(Dispatchers.IO) {
            stateFlow.state.collect {
                Log.i("minfo", "${Thread.currentThread().name} + $it")
            }
        }

        launch(Dispatchers.IO) {
            stateFlow.state.collect {
                Log.i("minfo", "${Thread.currentThread().name} + $it")

            }
        }
    }

打印结果:

 I  DefaultDispatcher-worker-1 + unKnown
 I  DefaultDispatcher-worker-2 + unKnown
// 等待两秒
 I  DefaultDispatcher-worker-1 + result
 I  DefaultDispatcher-worker-1 + result

StateFlow 的使用方式与 LiveData 类似。
MutableStateFlow 是可变类型的,即可以改变 value 的值。 StateFlow 则是只读的。这与 LiveData、MutableLiveData一样。

为什么使用 StateFlow

我们知道 LiveData 有如下特点:

1.只能在主线程更新数据,即使在子线程通过 postValue()方法,最终也是将值 post 到主线程调用的 setValue()
2.LiveData 是不防抖的
3.LiveData 的 transformation 是在主线程工作
4.LiveData 需要正确处理 “粘性事件” 问题。
鉴于此,使用 StateFlow 可以轻松解决上述场景。

class StateFlow {

    private val _state = MutableStateFlow<String>("unKnown")
    val state: kotlinx.coroutines.flow.StateFlow<String> get() = _state

    fun getApi2(scope: CoroutineScope) {
        scope.launch {
            delay(2000)
            _state.value = "hello, coroutine"
        }
    }

    fun getApi3(scope: CoroutineScope) {
        scope.launch {
            delay(2000)
            _state.value = "hello, kotlin"
        }
    }
}

    fun stateFlowTest2() = runBlocking<Unit> {
        val stateFlow: StateFlow = StateFlow()

        stateFlow.getApi2(this) // 开始获取结果
        delay(1000)
        stateFlow.getApi3(this) // 开始获取结果

        val job1 = launch(Dispatchers.IO) {
            delay(8000)
            stateFlow.state.collect {
                Log.i("minfo", "${Thread.currentThread().name} + $it")
            }
        }
        val job2 = launch(Dispatchers.IO) {
            delay(8000)
            stateFlow.state.collect {
                Log.i("minfo", "${Thread.currentThread().name} + $it")
            }
        }

        // 避免任务泄漏,手动取消
        delay(10000)
        job1.cancel()
        job2.cancel()
    }

现在的场景是,先请求 getApi1(), 一秒之后再次请求 getApi2(), 这样 stateFlow 的值加上初始值,一共被赋值过 3 次。确保,三次赋值都完成后,我们再收集 StateFlow 中的数据。
打印结果:

 DefaultDispatcher-worker-1 + hello, kotlin
 DefaultDispatcher-worker-2 + hello, kotlin

结果显示了,StateFlow 只会将最新的数据发射给订阅者。对比 LiveData, LiveData 内部有 version 的概念,对于注册的订阅者,会根据 version 进行判断,将历史数据发送给订阅者。即所谓的“粘性”。而SharedFlow可以做到此种粘性。

如需将任何数据流转换为 StateFlow,请使用stateIn中间运算符。

StateFlow、Flow 和 LiveData

StateFlow 和LiveData具有相似之处。两者都是可观察的数据容器类,并且在应用架构中使用时,两者都遵循相似模式。

但请注意,StateFlow 和LiveData的行为确实有所不同:

  • StateFlow 需要将初始状态传递给构造函数,而 LiveData 不需要。
  • 当 View 进入 STOPPED 状态时,LiveData.observe() 会自动取消注册使用方,而从 StateFlow 或任何其他数据流收集数据的操作并不会自动停止。如需实现相同的行为,您需要从 Lifecycle.repeatOnLifecycle 块收集数据流。
SharedFlow

SharedFlow 也有两种类型:SharedFlow 和 MutableSharedFlow。
使用 sharedIn 方法可以将 Flow 转换为 SharedFlow。

public fun <T> MutableSharedFlow(
   replay: Int,   // 当新的订阅者Collect时,发送几个已经发送过的数据给它
   extraBufferCapacity: Int = 0,  // 减去replay,MutableSharedFlow还缓存多少数据
   onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND  // 缓存溢出时的处理策略,三种 丢掉最新值、丢掉最旧值和挂起
): MutableSharedFlow<T>

class SharedFlow {

    private val _state = MutableSharedFlow<Int>(replay = 3,
        extraBufferCapacity = 2)
    val state: MutableSharedFlow<Int> get() = _state

    fun getApi(scope: CoroutineScope) {
        scope.launch {
            for (i in 0..20) {
                delay(200)
                _state.emit(i)
                Log.i("minfo", "send data $i")
            }
        }
    }

    fun main() = runBlocking {
        getApi(this)

        val job = launch(Dispatchers.IO) {
            delay(3000)
            state.collect {
                Log.i("minfo", "collect  $it")
            }
        }
        delay(5000)
        job.cancel()
    }
}

打印结果:

send data 0
send data 1
send data 2
send data 3
send data 4
send data 5
send data 6
send data 7
send data 8
send data 9
send data 10
send data 11
send data 12
send data 13
collect  11
collect  12
collect  13
send data 14
collect  14
send data 15
collect  15
send data 16
collect  16
send data 17
collect  17
send data 18
collect  18
send data 19
collect  19
send data 20
collect  20

分析一下该结果:
SharedFlow 每 200ms 发射一次数据,总共发射 21 个数据出来,耗时大约 4s。
SharedFlow 的 replay 设置为 3, extraBufferCapacity 设置为2, 即 SharedFlow 的缓存为 5 。缓存溢出的处理策略是默认挂起的。
订阅者是在 3s 之后开始收集数据的。此时应该已经发射了 14 个数据,即 0-13, SharedFlow 的缓存为 5, 缓存的数据为 9-13, 但是,只给订阅者发送 3 个旧数据,即订阅者收集到的值是从 11 开始的。

StateFlow 和 SharedFlow 的使用场景

StateFlow 的命名已经说明了适用场景, StateFlow 只会向订阅者发射最新的值,适用于对状态的监听。
SharedFlow 可以配置对历史发射的数据进行订阅,适合用来处理对于事件的监听。

Channel

Flow底层使用的Channel机制实现,StateFlow、SharedFlow都是一对多的关系,如果上游发送者与下游UI层的订阅者是一对一的关系,可以使用Channel来实现,Channel默认是粘性的。

Channel使用特点:
每个消息只有一个订阅者可以收到,用于一对一的通信。

Channel使用示例:

//viewModel中
private val _loadingChannel = Channel<Boolean>()
val loadingFlow = _loadingChannel.receiveAsFlow()

private suspend fun loadStart() {
    _loadingChannel.send(true)
}

private suspend fun loadFinish() {
    _loadingChannel.send(false)
}

//UI层接收Loading信息
 mViewModel.loadingFlow.flowWithLifecycle2(this, Lifecycle.State.STARTED) { isShow ->
     mStatusViewUtil.showLoadingView(isShow)
 }

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin StateFlowKotlin协程库中的一种流(Flow)实现,用于在异步场景下处理状态的变化。StateFlow可用于代替LiveData或RxJava的Observable,提供了一种简洁且类型安全的方式来观察和响应状态的变化。 StateFlow是一个具有状态的流(flow),它可以发射新的值并保持最新的状态。与普通的Flow相比,StateFlow更适用于表示单一的可变状态,并且可以方便地在多个观察者之间共享。 StateFlow在使用上类似于普通的Flow,你可以使用`stateIn`函数将其转换为一个只读的SharedFlow,并使用`collect`或`conflate`等操作符来观察和处理状态的变化。 下面是一个使用StateFlow的示例代码: ``` import kotlinx.coroutines.* import kotlinx.coroutines.flow.* fun main() = runBlocking { val state = MutableStateFlow("Initial state") val job = launch { state.collect { value -> println("Received state: $value") } } state.value = "Updated state" job.cancel() } ``` 在上面的示例中,我们创建了一个MutableStateFlow对象并初始化为"Initial state"。然后使用`collect`函数来观察state的变化,并在状态发生变化时打印出新的值。我们通过修改`state.value`来更新状态,并在控制台上看到"Received state: Updated state"的输出。 总之,Kotlin StateFlow提供了一种方便的方式来处理状态的变化,并与Kotlin协程无缝集成,使得在异步场景下处理状态变化更加简洁和可靠。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值