【Android进阶宝典】Kotlin——SharedFlow 源码解析

MutableSharedFlow 创建方法

public fun <T> MutableSharedFlow(
    replay: Int = 0,
    extraBufferCapacity: Int = 0,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T> {
   
    // step1: replay 和 extraBufferCapacity 都需要大于0
    require(replay >= 0) {
    "replay cannot be negative, but was $replay" }
    require(extraBufferCapacity >= 0) {
    "extraBufferCapacity cannot be negative, but was $extraBufferCapacity" }
    
    // step2: 当 replay == 0 且 extraBufferCapacity == 0 只能是挂起溢出策略
    require(replay > 0 || extraBufferCapacity > 0 || onBufferOverflow == BufferOverflow.SUSPEND) {
   
        "replay or extraBufferCapacity must be positive with non-default onBufferOverflow strategy $onBufferOverflow"
    }
    
    val bufferCapacity0 = replay + extraBufferCapacity
    // step3: 2个大正数相加,得出的值为负数,矫正
    val bufferCapacity = if (bufferCapacity0 < 0) Int.MAX_VALUE else bufferCapacity0
    return SharedFlowImpl(replay, bufferCapacity, onBufferOverflow)
}

SharedFlowImpl 构造函数

  • replay: 重放数目,重放 bufferEndIndexreplay 个元素, bufferEndIndex-1 为最快的 collector
  • bufferCapacity: 缓存区总大小,用于决定最快的收集器和最慢收集器相差的最大差距,由 replayextraBufferCapacity 相加而得
  • onBufferOverFlow: 缓存溢出策略,可设置三种
    1. DROP_OLDEST: 丢弃最老的
    2. DROP_LASTEST: 丢弃最新的
    3. SUSEPND: 挂起 => 这个策略在溢出的时候不可 tryEmit

SharedFlowImpl 存储的状态和关键索引

  • buffer : 存放最近弹出 bufferSize 个元素和 queueSize 个排队发射器
  • queueSize: 排队发射器的数量
  • bufferSize: 最近弹出的元素,容量由 bufferCapacity 决定
  • minCollectorIndex: 活跃收集器的最小索引,没有活跃的收集器的时候等于 replayIndex,当源码中没有收集器的时候值为 bufferEndIndex, 不过有收集器的时候都会矫正
  • replayIndex: 新收集器获取的索引 => 因为需要回放最近 replay 个元素

SharedFlowImpl 计算状态

  • head(buffer的头索引): minOf(minCollectorIndex, replayIndex)
  • replaySize(当前存在的回放数量): (head + bufferSize - replayIndex).toInt()
  • totalSize(buffer的总大小): bufferSize + queueSize
  • bufferEndIndex(缓存弹出过的数据最后的索引): head + bufferSize
  • queueEndIndex(buffer最后的索引): head + bufferSize + queueSize

SharedFlowImpl#buffer 官方定义结构图

在这里插入图片描述

SharedFlowImpl 发射流程

SharedFlowImpl#emit(value:T): 发射

override suspend fun emit(value: T) {
   
    // step1: 尝试快速发射, 发射成功则为true
    if (tryEmit(value)) return
    // step2: 快速发射失败,则进入挂起等待发射
    emitSuspend(value)
}

SharedFlowImpl#tryEmit(value:T): true代表发射成功,false代表发射失败

override fun tryEmit(value: T): Boolean {
   
    var resumes: Array<Continuation<Unit>?> = EMPTY_RESUMES
    val emitted = synchronized(this) {
   
        // step1: 尝试是否能发送成功,发送成功,并找到对应 
        if (tryEmitLocked(value)) {
   
            // step2: 发射成功,得到可恢复collectors的协程
            resumes = findSlotsToResumeLocked(resumes)
            true
        } else {
   
            false
        }
    }
    // step3: 恢复 可恢复collectors 挂起的协程
    for (cont in resumes) cont?.resume(Unit)
    return emitted
}

SharedFlowImpl#emitSuspend(value:T): 发射挂起

private suspend fun emitSuspend(value: T) = suspendCancellableCoroutine<Unit> sc@{
    cont ->
    var resumes: Array<Continuation<Unit>?> = EMPTY_RESUMES
    val emitter = synchronized(this) lock@{
   
        // step1: 重新再试一次,成功则恢复对应协程
        if (tryEmitLocked(value)) {
   
            cont.resume(Unit)
            // step1.1: 得到可恢复 collect 协程
            resumes = findSlotsToResumeLocked(resumes)
            return@lock null
        }
        // step2: 生成 Emitter 进入bufferÿ
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值