Android Flow:你真的了解?在工作当中的运用~~通过光照例子来解释一下..

目录


一、工作中运用的例子:MutableStateFlow和MutableSharedFlow应该使用哪个?

二、冷热流是什么

一、工作中运用的例子


MutableStateFlow和MutableSharedFlow,value和emit

ini 复制代码
private val _lightValue = MutableStateFlow(0f)
val lightValue = _lightValue.asStateFlow()
kotlin 复制代码
override fun onSensorChanged(event: SensorEvent) {
    if (event.sensor.type == Sensor.TYPE_LIGHT) {
        val lux = event.values[0]
        _lightValue.value = lux
    }
}
ini 复制代码
private val _lightValue = MutableSharedFlow<Float>()
val lightValue = _lightValue.asSharedFlow()
kotlin 复制代码
override fun onSensorChanged(event: SensorEvent) {
    if (event.sensor.type == Sensor.TYPE_LIGHT) {
        val lux = event.values[0]
        coroutineScope.launch {
            _lightValue.emit(lux)
        }
    }
}
kotlin 复制代码
override fun initObserve() {
    // 设置光照变化回调
    lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED){
            LightSensorManager.lightValue.collect{lux ->
                mBinding.progressBar.setProgress((lux/2000)*100)//20000目标值。
                mBinding.lightValue.text = " ${"%.1f".format(lux)} Lux"
                mBinding.lightContent.text = updateBrightnessStatus(lux)
            }
        }

    }

}

**他们的区别,什么时候应该使用哪个?他们的底层原理又是如何的呢?执行又是如何,每次collect之后,他们是如何执行呢?**只有了解了这些以后,我们写的代码性能才能更高,才稳定。

1.1 MutableStateFlow

比如我们上面的例子,是读取手机系统的光照强度,而这个光照强度的数值,是持续变化的一个状态量

那么使用MutableStateFlow,会更加合适。因为他是新订阅者会立即收到当前最新值。

可以这样理解,比如这个状态量1,2,3,4,5这些值,那么当你collect以后,就会立马重新获取最新值。即使没有得到光亮值,也会有一个初始值。

他的引用场景非常广泛,比如 UI 状态、传感器当前值,只要是持续变化的都应该使用他。

valueemit()的使用差异

在这个例子当中,你使用那个都可以。 但协程内优先用emit(),非协程环境优先用value,协程内使用value可能抛出异常或导致 UI 更新问题。

  1. 多线程并发更新,协程内部,使用emit(),线程安全有保障,避免数据竞争
  2. 非协程环境(普通函数 / 回调),简单的同步更新,使用value无需启动协程,直接赋值更高效(少一层协程调度)

传感器的onSensorChanged回调本身运行在后台线程,且是非协程环境,直接使用value更合适

1.2 MutableSharedFlow

而MutableSharedFlow,虽然也可以在这个获取光照强度的例子中进行使用,但可能会存在一些问题,比如用户打开页面时,SharedFlow默认不会发送任何数据,UI 会一直显示空白,直到光照发生变化才会首次更新。这显然不符合用户预期 ------ 打开页面就应该看到当前的光照值。因为MutableSharedFlow不能设置初始值。

虽然要解决这个问题,也可以手动配置replay = 1(保留最近 1 条数据),但相比起来就会复杂很多。传感器可能在短时间内发送多次相同的 lux 值(如光照稳定时),SharedFlow会将这些重复值全部发送给订阅者,导致 UI 频繁无效刷新(即使数值没变,也会执行setText等操作)。

StateFlow会自动比较新旧值,只有当值真正变化时才通知订阅者,避免无效更新。

SharedFlow需要手动配置缓冲区策略(如extraBufferCapacityonBufferOverflow),否则在高频数据场景下可能出现:

  • 缓冲区满时发送方挂起(导致传感器回调阻塞)
  • 或数据丢失(取决于配置的溢出策略)

StateFlow无需这些配置,内部通过 "覆盖最新值" 的方式天然适配高频状态更新(光照传感器可能每秒触发多次)。

1.3原理和底层

MutableStateFlow

  • 必须初始化一个默认值(如0f),始终持有最新值
  • 新订阅者会立即收到当前最新值(无论何时订阅,都能获取 "现在" 的状态)
  • 自动过滤重复值(连续相同的 lux 值不会触发无效更新)
  • 状态数据(有当前值概念)→ 用StateFlow
  • 如传感器读数、UI 控件状态(开关状态、进度值)、用户信息等。这些数据的核心是 "当前值",且新订阅者需要立即知晓。

MutableSharedFlow

  • 默认没有初始值,也不持有最新值

  • 新订阅者只能收到订阅之后的新数据(默认配置下)

  • 不会自动过滤重复值(即使连续发送相同 lux,也会全部传递)

  • 事件流(一次性通知)→ 用SharedFlow

  • 如按钮点击、网络请求结果、日志输出等。这些数据的核心是 "发生过的事件",新订阅者无需关心订阅前的内容。

  • 无状态特性:默认情况下,发送的事件不会被保存,错过的事件无法重放

  • 背压处理 :当订阅者处理速度慢于事件产生速度时,根据配置的策略(如BUFFER_OVERFLOW_SUSPEND)决定是挂起、丢弃还是报错

    执行流程

markdown 复制代码
1. 协程中调用`_lightValue.emit(lux)` → 进入缓冲区  
2. 检查缓冲区状态 → 若未满则存入,否则根据策略处理  
3. 遍历所有活跃订阅者,将事件分发给每个`collect`协程  

在光照传感器场景中,数据本质是 "状态" 而非 "事件",因此StateFlow是更自然、更简洁的选择。SharedFlow虽然能通过配置实现类似功能,但会引入不必要的复杂性,属于 "用错了工具"。

二、冷流和热流


2.1 冷流

典型例子flow { ... }构建的普通流、网络请求流。

  • 核心特性无订阅者时不产生数据,数据生产与订阅者绑定。
scss 复制代码
// 冷流示例:只有collect时才会执行代码块
val coldFlow = flow {
    println("开始产生数据") // 无订阅时不会执行
    emit(1)
    emit(2)
}

// 首次订阅:打印"开始产生数据"并收到1、2
coldFlow.collect { ... }
// 第二次订阅:再次打印"开始产生数据"并收到1、2(重新执行)
coldFlow.collect { ... }

2.2 热流

典型例子StateFlowSharedFlow、传感器数据流、UI 事件流。

  • 核心特性无论是否有订阅者,都会持续产生数据,数据生产与订阅者解耦。

StateFlowSharedFlow本质上都是热流 ,这是它们与普通flow的核心区别:

  • 它们的数据产生不依赖订阅者(例如传感器无论是否被观察,都会持续产生数据)。
  • 多个订阅者可以共享同一个数据流(类似 "直播被多人观看")。
类型 热流特性细节
StateFlow 始终持有最新状态(有 "当前值"),新订阅者会立即收到最新值
SharedFlow 不持有状态(默认无 "当前值"),新订阅者默认只收到订阅后的新数据(可配置重放)

传感器本身是 "热数据源"------ 无论是否有代码监听(订阅),硬件都会持续产生光照数据。 StateFlow作为 "热流",完美适配这种特性:即使没有 UI 订阅,传感器数据仍会更新StateFlowvalue;当 UI 恢复订阅时,能立即拿到最新的光照值(当前状态)。

如果误用冷流(如普通flow),会出现问题:

scss 复制代码
// 错误示例:用冷流接收传感器数据
val lightColdFlow = flow {
    sensorManager.registerListener(...) // 冷流中注册传感器
    // 发射数据...
}

// 问题:每次collect都会重新注册传感器,导致重复监听
lightColdFlow.collect { ... } // 第一次collect:注册传感器
lightColdFlow.collect { ... } // 第二次collect:再次注册,导致数据重复
  1. 冷流:适合 "按需产生数据" 的场景(如网络请求、数据库查询),数据生产与订阅强绑定。
  2. 热流:适合 "持续产生数据" 的场景(如传感器、UI 事件、状态变化),数据生产与订阅解耦。
相关推荐
tangweiguo030519873 小时前
极致效率:用 Copilot 加速你的 Android 开发
android·copilot
阿华的代码王国4 小时前
【Android】Room数据库的使用
android·数据库·room
lichong9514 小时前
【混合开发】Android+Webview+VUE播放视频之视频解析工具mediainfo-Macos
android·macos·架构·vue·音视频·api·postman
翻滚丷大头鱼10 小时前
android View详解—View的刷新流程源码解析
android
zhangphil11 小时前
Android adb shell命令分析应用内存占用
android·adb
漠缠12 小时前
Android AI客户端开发(语音与大模型部署)面试题大全
android·人工智能
Lei活在当下12 小时前
一个基础问题:关于SDK初始化时机的选择
android
雨白15 小时前
Android 触摸反馈与事件分发原理解析
android
relis17 小时前
解密大语言模型推理:Prompt Processing 的内存管理与计算优化
android·语言模型·prompt