关于 MutableSharedFlow 的 tryEmit 和 emit 争议说法

这篇文章需要对 kotlin 的 flow 有一定了解,扫盲可以先看这个文章 Flow 扫盲

这是 MutableSharedFlow 关于 tryEmit 和 emit 方法的定义。

kotlin 复制代码
override suspend fun emit(value: T)

public fun tryEmit(value: T): Boolean
  • 争议说法1:

    tryEmit 返回 true 代表发送成功,返回false 代表发送失败。

  • 争议说法2:

    tryEmit 是一个非挂起方法,这也是 tryEmit 和 emit 方法的根本区别。

先摆源码,然后再问题:

kotlin 复制代码
override fun tryEmit(value: T): Boolean {
    var resumes: Array<Continuation<Unit>?> = EMPTY_RESUMES
    val emitted = synchronized(this) {
        if (tryEmitLocked(value)) {
            resumes = findSlotsToResumeLocked(resumes)
            true
        } else {
            false
        }
    }
    for (cont in resumes) cont?.resume(Unit)
    return emitted
}
kotlin 复制代码
@Suppress("UNCHECKED_CAST")
private fun tryEmitLocked(value: T): Boolean {
    if (nCollectors == 0) return tryEmitNoCollectorsLocked(value) // always returns true
    if (bufferSize >= bufferCapacity && minCollectorIndex <= replayIndex) {
        when (onBufferOverflow) {
            BufferOverflow.SUSPEND -> return false // will suspend
            BufferOverflow.DROP_LATEST -> return true // just drop incoming
            BufferOverflow.DROP_OLDEST -> {} // force enqueue & drop oldest instead
        }
    }
    enqueueLocked(value)
    bufferSize++ // value was added to buffer
    if (bufferSize > bufferCapacity) dropOldestLocked()
    if (replaySize > replay) { // increment replayIndex by one
        updateBufferLocked(replayIndex + 1, minCollectorIndex, bufferEndIndex, queueEndIndex)
    }
    return true
}

问题

q1:使用 tryEmit 能发送成功么?

kotlin 复制代码
private val _testFlow = MutableSharedFlow<Boolean>()  
val testFlow: SharedFlow<Boolean> = _testFlow  
  
fun sendNew(newDat:Boolean){  
    _testFlow.tryEmit(newDat)  
}

fun registerFlowObser(){
    launchCoroutine {  
        testFlow.collect {  
            Log.e("zly", "testFlow--$it")  
        }  
    }
}

这里是发送失败的,bufferSize >= bufferCapacity && minCollectorIndex <= replayIndex 条件下,判断为 BufferOverflow.SUSPEND ,然后 return false。

「曾经组内同学就这么定义的 flow 并且使用的 tryEmit,找了很久 才知道原来 数据根本就没发出去...)」

所以 tryEmit 并不适用于这种场景。_testFlow 这种定义 只能使用 emit 方法。

q2:返回true,就是发送成功了么

kotlin 复制代码
private val _testFlow = MutableSharedFlow<String>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)  
val testFlow: SharedFlow<String> = _testFlow 
  
fun sendNew(newDat:String){  
    val result = _testFlow.tryEmit(newDat)  
    Log.e("zly","send ----- $newDat -- result=$result")
}

fun registerFlowObser(){
    launchCoroutine {  
        testFlow.collect {  
            delay(2000)
            Log.e("zly", "testFlow--$it")  
        }  
    }
}

连续快速执行 sendNew 方法,每次 result 都是 true,通过源码 BufferOverflow.DROP_OLDEST -> {} 可以看出 使用这个策略的case 下,始终都会返回true,但数据不一定能够正确处理。所以「发送成功」!= 「发送成功且被collector 成功接收」

q3:emit 和 tryEmit 方法区别

kotlin 复制代码
override suspend fun emit(value: T) {  
    if (tryEmit(value)) return // fast-path  
    emitSuspend(value)  
}

emit 能够确保数据的发送,且是先看调用 tryEmit 方法,发送失败,则再调用 emit 确保数据发送成功。

q4: curValue 的所有引用都在如下代码中,是否需要 Volatile 修饰?

kotlin 复制代码
@Volatile
private var curValue = false

launchCoroutine {
//playStateUiState 是一个 stateflow
    playStateUiState.collect {
        if (curValue != true) {
            // 执行具体业务逻辑
        }
        curValue = it.needForceUpdate
    }
}

这是我最近写的需求中涉及到的代码,在我写系统性看 Java 和 Kotlin 中的锁这篇文章的时候,注意到这段代码,才发现手欠写了 @Volatile 这个注解,其实这种 case 下是完全没必要加的,因为 curValue 的读取和赋值都在 collect 代码块中,顺序执行,不存在竞争条件。

总结

如何选择使用 tryEmit 还是 emit 方法?

  • 根据场景来选择使用tryEmit 还是 emit。eg:直播间场景的评论区展示的评论适合使用 tryEmit,在高频case下允许数据丢失
  • 根据选择的方法,定义合适的 MutableSharedFlow

eg:直播间的评论区消息的定义

kotlin 复制代码
private val _highFrequencyFlow by lazy { MutableSharedFlow<Bean>(0, HIGH_FREQUENCY_MAX, BufferOverflow.DROP_OLDEST) }  
val highFrequencyFlow :SharedFlow<Bean> = _highFrequencyFlow

fun sendChat(event:Bean){
    _highFrequencyFlow.tryEmit(event)
}

tryEmit 返回true,不代表数据被接收者成功接收。

ps.这次为了参加掘金的活动,破天荒,这么快总结完,草稿都已经存了一年了...欢迎大家 点赞+收藏+批评~

相关推荐
yours_Gabriel1 小时前
【java面试】微服务篇
java·微服务·中间件·面试·kafka·rabbitmq
这个家伙很笨2 小时前
了解Android studio 初学者零基础推荐(4)
android·ide·android studio
alexhilton4 小时前
在Android应用中实战Repository模式
android·kotlin·android jetpack
天涯学馆4 小时前
工厂模式在 JavaScript 中的深度应用
前端·javascript·面试
巛、5 小时前
ES6面试题
前端·面试·es6
汪子熙6 小时前
走进 Fundamental NGX Platform:从 SAP 设计体系到高生产力组件层
前端·javascript·面试
拉不动的猪6 小时前
单点登录全流程小姐
前端·javascript·面试
半桔6 小时前
【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
linux·运维·服务器·面试·centos
二流小码农8 小时前
鸿蒙开发:DevEcoTesting中的稳定性测试
android·ios·harmonyos