一文详解 Kotlin 中的流- flow、channel

20 阅读2分钟

一、前言

当下 Android 比较流行的架构是 MVI + Kotlin 协程 + Compose,在 MVI 的架构中。为了减少 LiveData 在复杂页面过多的问题,我们推荐使用 State 进行统一管理。

LiveData 是 UI Layer 所观察的对象,在页面销毁的时候,能够自动取消所有的观察者,避免内存泄漏。和 LiveData 相比, Flow具有如下优势:

  1. 支持背压
  2. 支持数据变换
  3. 支持线程切换

总之一句话,Flow 天生为协程而生。

二、流的分类

Kotlin 的流可以分为两类,冷流和热流。冷流和热流的区别如下:

冷流:没有消费者,生产者就不会生产数据。

热流:是否生产数据,和消费者没有关系

在 Kotlin 协程中,Flow 是冷流,StateFlow、SharedFlow、Channel 等是热流。StateFlow 用于页面状态,SharedFlow 用于事件,Channel 主要用于协程间进行通信。

三、flow、channel 的用法

flow 的用法如下:

// ViewModel
fun getData(): Flow<String> = flow {
    emit("")
}.flowOn(Dispatchers.IO).catch {
    println(it.message)
}

// Activity
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        vm.getData().collect {}
    }
}

StateFlow 和 SharedFlow用法如下:

private val _stateFlow = MutableStateFlow(1)
val stateFlow: StateFlow<Int> = _stateFlow

private val _shareFlow = MutableSharedFlow<Int>()
val shareFlow : SharedFlow<Int> = _shareFlow

viewModelScope.launch {
    _stateFlow.value = 2
    _shareFlow.emit(1)
}

Channel 主要是用在协程之间进行通信,代码示例如下:

fun main() = runBlocking {
    GlobalScope.launch {
        val channel = Channel<Int>()
        val scope = CoroutineScope(Job())
        scope.launch {
            launch {
                channel.send(1)
            }
            launch {
                println(channel.receive())
            }
        }.join()
    }.join()
}

Flow 还有其他的一些操作变换符,例如 map、tranform 等,不在这里一一详述。另外,Channel 是线程安全的。

四、需要注意什么

LiveDataStateFlow 在 Android 架构中,都是用于状态更新的,他们区别如下:

  1. LiveData 具有生命周期感知的能力,StateFlow 不具备,因此 StateFlow监听数据的时候,需要在生命周期里
  2. LiveData 不具备防抖的能力,StateFlow 具有防抖的能力

SharedFlowStateFlow 都是热流,StateFlow 是特殊 SharedFlow,他们的区别如下:

  1. 从作用场景来看,SharedFlow 用于事件更新,StateFlow 用于状态更新
  2. collect() 和 emit() 都是挂起函数,需要确保在协程内部进行调用
  3. SharedFlow emit() 和collect() 都是挂起函数,可能会阻塞当前的协程。因此生产者和消费者需要需要在不同的协程。