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 事件、状态变化),数据生产与订阅解耦。
相关推荐
安东尼肉店6 小时前
Android compose屏幕适配终极解决方案
android
2501_916007476 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun7 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户20187928316711 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子11 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
phoneixsky11 小时前
Kotlin的各种上下文Receiver,到底怎么个事
kotlin
小趴菜822711 小时前
安卓接入Max广告源
android
齊家治國平天下11 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO11 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
heeheeai11 小时前
okhttp使用指南
okhttp·kotlin·教程