Kotlin Flow 的 emit 和 tryEmit 有什么区别 ?

在 Kotlin Flow 中,emit()tryEmit() 都用于向 Flow 发送值,但它们的关键区别在于背压处理协程上下文

一、主要区别

1. emit()

  • 挂起函数:会挂起直到有空间接收值(处理背压)
  • 协程安全:只能在协程作用域中调用
  • 推荐使用 :在 collectcallbackFlow 中优先使用
kotlin 复制代码
flow {
    // 会挂起直到接收方准备好
    emit(1)
    emit(2)
}

2. tryEmit()

  • 非挂起函数 :尝试立即发射值,如果接收方未准备好则返回 false
  • 可能丢数据 :返回 false 时值会被丢弃
  • 有限场景:主要在 MutableStateFlow 和 MutableSharedFlow 中使用
kotlin 复制代码
val _state = MutableStateFlow(0)

fun updateValue(newValue: Int) {
    // 尝试发射,如果失败则丢弃
    val success = _state.tryEmit(newValue)
    if (!success) {
        // 处理发射失败的情况
    }
}

二、使用场景对比

1. 使用 emit() 的场景

kotlin 复制代码
// 1. 在 flow {} 构建器中
flow {
    for (i in 1..10) {
        delay(100)
        emit(i)  // ✅ 正确
    }
}

// 2. 在 callbackFlow 中
callbackFlow {
    callback.setOnData { data ->
        trySend(data)  // callbackFlow 特殊方法
    }
    awaitClose { callback.removeListener() }
}

2. 使用 tryEmit() 的场景

kotlin 复制代码
// 主要在 MutableStateFlow/MutableSharedFlow 中
class ViewModel {
    private val _uiState = MutableStateFlow<UiState>(Loading)
    val uiState: StateFlow<UiState> = _uiState
    
    fun update() {
        // 非挂起函数中尝试更新状态
        if (!_uiState.tryEmit(Success(data))) {
            // 处理背压(通常日志记录)
        }
    }
}

3. 下面是一个小表格,可以帮助快速了解何时使用 emittryEmit

场景 使用 emit 使用 tryEmit
在挂起函数内部 避免使用(除非非常紧急)
UI 点击或生命周期事件 可能(如果需要快速更新)
回调、监听器或后台线程 避免使用(不能挂起)
需要保证数据送达 否(需自行处理失败情况)
发送即忘(fire-and-forget)场景

两句话概括就是:

  • 当你需要可靠的、对协程友好的发送方式时,使用 emit
  • 当你希望实现无阻塞、快速触发且不暂停的操作时,使用 tryEmit

三、重要注意事项

  1. callbackFlow 的特殊情况

    kotlin 复制代码
    callbackFlow {
        callback { data ->
            // 这里必须用 trySend,因为 callback 不是挂起上下文
            trySend(data)
        }
    }
  2. MutableSharedFlow 的配置影响

    kotlin 复制代码
    val flow = MutableSharedFlow<Int>(
        replay = 0,
        extraBufferCapacity = 10  // 缓冲区大小影响 tryEmit 成功率
    )
  3. 性能考虑

    • emit():更安全,保证数据不丢失
    • tryEmit():性能更好,但可能丢失数据

总结一下二者的区别:

特性 emit() tryEmit()
挂起函数
等待收集者 是(如果缓冲区已满)
返回类型 Unit Boolean
是否立即发射? 否(可能会挂起) 是(如果缓冲区允许)
是否可在协程中使用? 必须 不需要

四、最佳实践建议

  • 优先使用 emit() ,除非有特殊性能需求
  • flow {} 构建器中总是使用 emit()
  • 在回调接口中(如 callbackFlow)使用 trySend()/tryEmit()
  • 对于状态更新,如果无法确保在协程上下文中,使用 tryEmit() 但要有失败处理
  • 明确处理 tryEmit() 返回的 false 情况

简单总结:emit() 是安全的阻塞版本,tryEmit() 是非阻塞的尝试版本,可能失败丢数据。

相关推荐
胖虎13 小时前
Android 文件下载实践:基于 OkHttp 的完整实现与思考
android·okhttp·下载文件·安卓下载·安卓中的下载
_李小白3 小时前
【Android 美颜相机】第四天:CameraLoader、Camera1Loader 与 Camera2Loader
android·数码相机
00后程序员张3 小时前
iOS APP 性能测试工具,监控CPU,实时日志输出
android·ios·小程序·https·uni-app·iphone·webview
YIN_尹3 小时前
【MySQL】数据类型(下)
android·mysql·adb
invicinble3 小时前
认识es的多个维度
android·大数据·elasticsearch
前端切图仔0013 小时前
Chrome 扩展程序上架指南
android·java·javascript·google
黄林晴3 小时前
Compose Multiplatform 1.10.0 重磅发布!三大核心升级,跨平台开发效率再提升
android·android jetpack
锁我喉是吧4 小时前
Android studio 编译faiss
android·android studio·faiss
鹏程十八少4 小时前
3. Android 腾讯开源的 Shadow,凭什么成为插件化“终极方案”?
android·前端·面试
TheNextByte14 小时前
如何通过蓝牙将联系人从Android传输到 iPhone
android·cocoa·iphone