这篇文章需要对 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.这次为了参加掘金的活动,破天荒,这么快总结完,草稿都已经存了一年了...欢迎大家 点赞+收藏+批评~