Android-Flow 使用和源码解析

本文深入探讨了Android中Kotlin的Flow数据流的使用,包括创建、修改和收集数据的方法,以及冷流和热流的概念。重点介绍了StateFlow、SharedFlow的区别,以及如何在Android环境中有效利用Flow,如repeatOnLifecycle和flowWithLifecycle方法。同时,文章通过源码分析帮助读者更好地理解Flow的工作原理。

概述

数据流以协程为基础构建,可提供多个值。从概念上来讲,数据流是可通过异步方式进行计算处理的一组数据序列。所发出值的类型必须相同。例如,Flow 是发出整数值的数据流。

在协程中,与仅返回单个值的挂起函数相反,数据流可按顺序发出多个值。

数据流包含三个实体:

  • 提供方会生成添加到数据流中的数据。得益于协程,数据流还可以异步生成数据。
  • (可选)中介可以修改发送到数据流的值,或修正数据流本身。
  • 使用方则使用数据流中的值。

在这里插入图片描述

1.使用

1.1 创建数据流

创建数据流有两种常用方式: 第一就是试用数据流构建函数, 第二就是利用扩展方法将原有类型转换成Flow

使用数据构建函数

这里比较常用的函数是 flow()flowOf()

val f = flow {
    emit(1)
}
val f2 = flowOf("abc")
val f3 = flowOf("A", "B", "C")

其实 flowof() 是简化版的 flow() 函数,内部帮我们调用了 emit()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JEWWqcWq-1670402003040)(/Users/apple/Downloads/job_files/flow/Xnip2022-11-20_00-25-28.jpg )]

注意:

使用 flow 构建器时,提供方不能提供来自不同 CoroutineContextemit 值。因此,请勿通过创建新协程或使用 withContext 代码块,在不同 CoroutineContext 中调用 emit 可以使用 flowOn 或者 callbackFlow

使用扩展方法

扩展方法是 kotlin 标准库中提供的 asFlow() . 目前基本上覆盖了基本数据类型、lambda、及挂起函数等

下面看一下具体如何使用:

普通lambda 表达式
fun testAsFlow1(blacking: () -> String) {
    val flow = blacking.asFlow()
}
挂起函数
suspend fun testAsFlow2(blacking: suspend () -> String) {
    val flow = blacking.asFlow()
}

或者以下方式:

suspend fun remoteCall(): String = withContext(Dispatchers.IO) {
    return@withContext "ABC"
}
fun remoteCallFlow(): Flow<String> = ::remoteCall.asFlow()
基本数据类型

kotlin 标准库中提供了基本数据类型相关扩展方法,其中 Map类型不能直接使用asFlow() 需要转换成其支持的类型,再使用 asFlow().

fun testAsFlow3() {

    // Range 类型
    val flow1 = IntRange(1, 100).asFlow()
    val flow2 = LongRange(1L, 1000L).asFlow()
    
    // Array 类型
    val flow3 = intArrayOf(1, 2, 3).asFlow()
    val flow4 = longArrayOf(1L, 2L, 3L).asFlow()
    val flow5 = arrayOf("A", "B", "C").asFlow()
    
    // Sequence 类型
    val flow6 = sequenceOf("X", "Y", "Z").asFlow()
    
    // Iterable 类型
    val flow7 = listOf(1, 2, 3).asFlow()
    val flow8 = setOf("E", "G", "C").asFlow()
    
    // Iterator 类型
    val flow9 = iterator<Int> {}.asFlow()
    
    // Map 类型
	val map = hashMapOf(Pair("A", 1), Pair("B", 2))
	
	val flow10 = map.asIterable().asFlow()
	val flow11 = map.asSequence().asFlow()
	val flow12 = map.iterator().asFlow()
}

1.2 修改数据流

可以利用中间运算符在不使用值的情况下修改数据流。中间运算符可以接连应用,形成链式运算,在数据项被发送到数据流时延迟执行。

其中操作符还分为三大类:

  1. 创建操作符
  2. 中间操作符
  3. 末端操作符

在这里插入图片描述
关于Flow 操作符可以查看这篇文章 Flow 操作符大全

1.3 收集数据

使用 collect 进行收集数据

val f = emptyFlow<Int>()
    .collect {
        // ignore 
    }

2. 冷流 与 热流

所谓 冷流 和 热流 是根据数据收集时是否出发提供方代码而定义的。

冷流: 即 收集数据时才触发提供方代码,即为冷流。我们使用 flowflowOf 创建的 Flow 都是冷流。

热流: 数据收集时数据已经存在,不需要触发提供方代码,这样的流即为 热流。StateFlowSharedFlow 这两个都是热流。

StateFlow

StateFlow 是热数据流:从数据流收集数据不会触发任何提供方代码。StateFlow 始终处于活跃状态并存于内存中,而且只有在垃圾回收根中未涉及对它的其他引用时,它才符合垃圾回收条件。

StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新。

在 Android 中,StateFlow 非常适合需要让可变状态保持可观察的类。

// StateFlow
private val _uiStateMutableStateFlow: MutableStateFlow<UIState> = MutableStateFlow(UIState.INITIAL)
val uiStateStateFlow: StateFlow<UIState> = _uiStateMutableStateFlow

StateFlow、Flow 和 LiveData

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

StateFlowLiveData 的行为确实有所不同:

  1. StateFlow 需要将初始状态传递给构造函数,而 LiveData 不需要。
  2. View 进入 STOPPED 状态时,LiveData.observe() 会自动取消注册使用方,而从 StateFlow 或任何其他数据流收集数据的操作并不会自动停止。

SharedFlow

SharedFlowStateFlow 的可配置性极高的泛化数据流, StateFlow 本身继承于 SharedFlow

private val _uiStateSharedFlow: MutableSharedFlow<UIState> = MutableSharedFlow(1, 3, BufferOverflow.DROP_OLDEST)
val uiStateSharedFlow: SharedFlow<UIState> = _uiStateSharedFlow

创建 SharedFlow 需要三个参数:

  • replay : 重新给订阅者发送之前值的数量,默认是 0
  • extraBufferCapacity : 缓冲区数量, 默认为 0 。不包含 replay 配置数量
  • onBufferOverflow : 缓冲区数据存满发送时策略,BufferOverflow 类型枚举,默认值 BufferOverflow.SUSPEND

BufferOverflow 有三种策略:

  1. SUSPEND : 当缓冲区已满时,正在发送或发出值的上游被挂起。
  2. DROP_OLDEST : 缓冲区满时删除缓冲区中最旧的值,将新值添加到缓冲区,不进行挂起。
  3. DROP_LATEST :缓冲区满时删除立即添加到缓冲区的最新值(以便缓冲区内容保持不变),不进行挂起。

冷流和热流转换

shareIn

将普通flow 转化为 SharedFlow , 其有三个参数

  • scope: CoroutineScope 开始共享的协程范围
  • started: SharingStarted 控制何时开始和停止共享的策略
  • replay: Int = 0 发给 新的订阅者 的旧值数量

其中 started 有一些可选项:

  • Eagerly : 共享立即开始,永不停止
  • Lazily : 当第一个订阅者出现时, 永不停止
  • WhileSubscribed : 在第一个订阅者出现时开始共享,在最后一个订阅者消失时立即停止(默认情况下),永久保留重播缓存(默认情况下)

WhileSubscribed 具有以下可选参数:

  • stopTimeoutMillis — 配置最后一个订阅者消失到协程停止共享之间的延迟(以毫秒为单位)。 默认为零(立即停止)。
  • replayExpirationMillis - 共享的协程从停止到重新激活,这期间缓存的时效
val shareIn = flowOf(1, 2, 3)
    .shareIn(viewModelScope, SharingStarted.Eagerly, 10)
stateIn

将普通flow 转化为 StateFlow 。 其有三个参数:

  • scope - 开始共享的协程范围
  • started - 控制何时开始和停止共享的策略
  • initialValue - 状态流的初始值
val stateIn = flowOf(1)
    .stateIn(viewModelScope, SharingStarted.Lazily, 1)

3. Android 中使用Flow

由于 Flow 流的发送和接收只是在 协程生命周期中,而没有和 Android 的一些生命周期组件,如 Activity/Fragment 绑定,所以 Android 推出了与 Lifecycle 生命周期组件相关的库和扩展方法,以方便我们在合理的生命周期内收集数据。

repeatOnLifecycle

repeatOnLifecycleLifecycle 生命周期组件,扩展方法。用于在合适的生命周期期间接收 flow 数据 。

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        uiStateFlow.collect { uiState ->
               updateUi(uiState)
          }
    }
}
源码分析
public suspend fun Lifecycle.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
) {
   // 如果 Lifecycle 组件处于 INITIALIZED 则直接抛出错误
    require(state !== Lifecycle.State.INITIALIZED) {
        "repeatOnLifecycle cannot start work with the INITIALIZED lifecycle state."
    }

    // 如果 Lifecycle 组件处于 DESTROYED 停止
    if (currentState === Lifecycle.State.DESTROYED) {
        return
    }

    // 开启一个协程,并切换到主线程中,后续所有代码均运行在此线程中
    coroutineScope {
        withContext(Dispatchers.Main.immediate) {
            // 再次检查当前生命周期状态,如果是 DESTROYED 则取消
            if (currentState === Lifecycle.State.DESTROYED) return@withContext

		    // 协程
            var launchedJob: Job? = null

            // 生命周期观察回调
            var observer: LifecycleEventObserver? = null
            try {
                // 创建一个协程,这个协程除非生命周期结束或者被取消,否则一直处于被挂起状态
                suspendCancellableCoroutine<Unit> { cont ->
                    // 检查生命周期执行区间
                    val startWorkEvent = Lifecycle.Event.upTo(state)
                    val cancelWorkEvent = Lifecycle.Event.downFrom(state)
                    
                    val mutex = Mutex()
                    // 创建 Lifecycle 生命周期观察者
                    observer = LifecycleEventObserver { _, event ->
                        if (event == startWorkEvent) {
                            // Launch the repeating work preserving the calling context
                            launchedJob = this@coroutineScope.launch {
                                // 重新开启协程执行,lambda 方法块
                                mutex.withLock {
                                    coroutineScope {
                                        block()
                                    }
                                }
                            }
                            return@LifecycleEventObserver
                        }
                        // 取消协程
                        if (event == cancelWorkEvent) {
                            launchedJob?.cancel()
                            launchedJob = null
                        }
                        // 关闭外部 协程,内部使用状态机
                        if (event == Lifecycle.Event.ON_DESTROY) {
                            cont.resume(Unit)
                        }
                    }
                    this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)
                }
            } finally {   // 取消协程 & 移除观察者
                launchedJob?.cancel()
                observer?.let {
                    this@repeatOnLifecycle.removeObserver(it)
                }
            }
        }
    }
}

以上就是 repeatOnLifecycle 方法的源码,注释比较详细,总的来说,就两点:

  1. 开启协程,创建 Lifecycle 观察者,在符合生命周期期间进行执行外部代码块
  2. 生命周期结束之后,取消协程、移除观察者等

需要注意的是:

在调用 repeatOnLifecycle()state 不能传入 Lifecycle.State.INITIALIZEDLifecycle.State.DESTROYED

另外 LifecycleOwner 也有同名扩展方法,更加方便使用,其内部也是调用 Lifecycle的扩展方法。

public suspend fun LifecycleOwner.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)
flowWithLifecycle

flowWithLifecycleFlow 的扩展方法,为方便Flow 使用,其内部也是通过 LifecyclerepeatOnLifecycle() 实现,但还是有点区别。

使用:

lifecycleScope.launch {
    flow.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
    	 .filter {  }
         .map {  }
         .collect {
             // Consume flow emissions
     }
}

从使用上看 flowWithLifecycle() 更像是一个操作符,因为他返回的还是 Flow 。还可以继续使用其他操作符,最后再使用 末端操作符来接收值。

public fun <T> Flow<T>.flowWithLifecycle(
    lifecycle: Lifecycle,
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED
): Flow<T> = callbackFlow {
    lifecycle.repeatOnLifecycle(minActiveState) {
        this@flowWithLifecycle.collect {
        	  // 将值重新发送到 flow 中
            send(it)
        }
    }
    // 关闭 flow 
    close()
}

flowWithLifecycle 的源码比较简单,内部创建一个 callbackFlow 用于将 ifecycle.repeatOnLifecycle() 收集结果,再发送至 Flow 中,这样下游依然能使用 collect 来收集上游的结果。最后在 协程关闭时将 flow 关闭掉 。

参考

https://developer.android.com/kotlin/flow?hl=zh-cn

https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/

https://kotlinlang.org/docs/flow.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值