前言
计步器、体感游戏、手势识别、跌倒检测——这些功能都依赖 Android 传感器框架。但很多工程师只学到 registerListener 就开始写,结果遇到高频数据丢帧、功耗爆炸、精度不稳定等问题。
这一篇讲传感器框架的核心机制:采样率、批处理模式、传感器类型选型,以及高频数据处理的工程实践。
一、传感器体系架构
+--------+ +------------+ +-----------+
| App | <-- | Framework | <-- | HAL |
| SensorM| Event| SensorSvc | Data | SensorHw |
|anager | 回调 | | | |
+--------+ +------------+ +-----------+
|
+--------+
| 物理 |
| 传感器 |
+--------+
硬件传感器:物理芯片(加速度计、陀螺仪、磁力计)
虚拟传感器:软件合成(重力、线加速度、旋转向量),由系统从硬件数据计算
合成传感器:特殊用途合成(计步器、显著运动检测)
二、传感器类型选型
Android 定义了 30+ 种传感器类型,常用的几个:
选型原则:
// 坏:用原始加速度计自己算去重力
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
// 好:直接用系统合成的线加速度
sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
// 坏:用 TYPE_ROTATION_VECTOR(含磁力,室内有磁场干扰)
// 好:游戏场景用 TYPE_GAME_ROTATION_VECTOR(纯陀螺仪+加速度)
sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
系统合成传感器背后的算法是 Google 调优过的,比自己手写的低通滤波质量好。
三、注册监听
class MotionDetector(context: Context) {
private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
private var accelerometer: Sensor? = null
private var gyroscope: Sensor? = null
// 传感器数据缓存
private val accelData = FloatArray(3)
private val gyroData = FloatArray(3)
private val sensorListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
when (event.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> {
// 注意:这里直接引用 event.values 不安全
// event.values 会被系统复用,下个事件会覆盖
System.arraycopy(event.values, 0, accelData, 0, 3)
processMotion()
}
Sensor.TYPE_GYROSCOPE -> {
System.arraycopy(event.values, 0, gyroData, 0, 3)
processMotion()
}
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
// 精度变化:通常可以忽略
// accuracy: SensorManager.SENSOR_STATUS_*
}
}
fun start() {
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
accelerometer?.let { sensor ->
// samplingPeriodUs: 采样周期(微秒)
// 选项:SENSOR_DELAY_NORMAL(200ms), UI(16ms), GAME(20ms), FASTEST(0-1ms)
// 实际采样率取决于硬件,这里只是"期望值"
sensorManager.registerListener(
sensorListener,
sensor,
SensorManager.SENSOR_DELAY_GAME // ~50Hz
)
}
}
fun stop() {
sensorManager.unregisterListener(sensorListener)
}
private fun processMotion() {
// 在这里处理传感器数据
}
}
四、采样率真相
registerListener 的第三个参数(采样周期)不是"精确的采样间隔",而是一个"最高频率建议"。实际采样率由硬件决定。
// 指定具体微秒值
sensorManager.registerListener(
sensorListener,
accelerometer,
10000 // 10ms = 100Hz
)
// 但实际可能跑 120Hz(硬件原生频率)
// 系统会把 100Hz 的数据"下采样"到接近 100Hz 给你
不同传感器的原生频率不同:
五、批处理模式(Batch Mode)
Android 4.4+ 支持批处理:硬件 FIFO 缓存多个事件,App 一次性读取。减少唤醒次数,省电。
// 普通模式:每个事件都唤醒 App
sensorManager.registerListener(
sensorListener,
accelerometer,
SensorManager.SENSOR_DELAY_GAME
)
// 批处理模式:最多缓存 maxReportLatencyUs 的数据
sensorManager.registerListener(
sensorListener,
accelerometer,
SensorManager.SENSOR_DELAY_GAME,
5_000_000 // 最大报告延迟 5 秒
)
批处理模式下,如果 App 没有读取,硬件会在 FIFO 接近满时唤醒 App。CPU 唤醒次数大幅减少。
适用场景:后台计步、运动追踪。不适用于实时体感控制(延迟太大)。
六、高频数据处理实战
场景:手势识别(100Hz 加速度 + 200Hz 陀螺仪)
class GestureRecognizer(context: Context) {
private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
// 环形缓冲区:存储最近 1 秒的数据
private val accelBuffer = CircularBuffer(300) // 100Hz * 3s
private val gyroBuffer = CircularBuffer(600) // 200Hz * 3s
private val sensorListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
when (event.sensor.type) {
Sensor.TYPE_LINEAR_ACCELERATION -> {
accelBuffer.push(event.values[0], event.values[1], event.values[2])
tryDetectGesture()
}
Sensor.TYPE_GYROSCOPE -> {
gyroBuffer.push(event.values[0], event.values[1], event.values[2])
}
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
private var lastDetectTime = 0L
private fun tryDetectGesture() {
val now = System.currentTimeMillis()
if (now - lastDetectTime < 100) return // 节流:100ms 检测一次
lastDetectTime = now
// 取最近 0.5 秒数据
val accelWindow = accelBuffer.lastN(50) // 100Hz * 0.5s
val gyroWindow = gyroBuffer.lastN(100) // 200Hz * 0.5s
// 特征提取
val accelMag = accelWindow.map { sqrt(it.x*it.x + it.y*it.y + it.z*it.z) }
val gyroMag = gyroWindow.map { sqrt(it.x*it.x + it.y*it.y + it.z*it.z) }
// 峰值检测(简单版)
val maxAccel = accelMag.max()
val maxGyro = gyroMag.max()
when {
maxAccel > 15.0f && maxGyro > 3.0f -> {
// 摇晃手势
onGestureDetected(Gesture.SHAKE)
}
maxAccel > 8.0f && maxGyro < 1.0f -> {
// 拍击手势
onGestureDetected(Gesture.TAP)
}
maxGyro > 2.0f && maxAccel < 5.0f -> {
// 翻转手势
onGestureDetected(Gesture.FLIP)
}
}
}
fun start() {
val accel = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
val gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
sensorManager.registerListener(sensorListener, accel, 10000) // 100Hz
sensorManager.registerListener(sensorListener, gyro, 5000) // 200Hz
}
}
data class AccelSample(val x: Float, val y: Float, val z: Float)
enum class Gesture { SHAKE, TAP, FLIP }
// 简单环形缓冲区
class CircularBuffer(private val capacity: Int) {
private val buffer = ArrayDeque<AccelSample>()
private val lock = Any()
fun push(x: Float, y: Float, z: Float) {
synchronized(lock) {
buffer.addLast(AccelSample(x, y, z))
if (buffer.size > capacity) buffer.removeFirst()
}
}
fun lastN(n: Int): List<AccelSample> {
synchronized(lock) {
return buffer.takeLast(minOf(n, buffer.size))
}
}
}
七、功耗优化
高频传感器监听是耗电大户。优化策略:
class PowerAwareMotionDetector(context: Context) {
// 策略:用低频触发器 + 高频采集
private val significantMotion: Sensor? = sensorManager
.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
private val triggerListener = object : TriggerEventListener() {
override fun onTrigger(event: TriggerEvent) {
// 检测到大动作 -> 切换到高频采集
startHighFreqCollection()
// 采集完再重新注册触发器
sensorManager.requestTriggerSensor(this, significantMotion)
}
}
private fun startHighFreqCollection() {
// 注册高频加速度计,采集 3 秒后自动停止
sensorManager.registerListener(
highFreqListener,
accelerometer,
SensorManager.SENSOR_DELAY_FASTEST
)
handler.postDelayed({
sensorManager.unregisterListener(highFreqListener)
}, 3000)
}
}
功耗对比:
小结
传感器开发的核心是"选对传感器 + 设对采样率 + 处理好数据线程"。优先用系统合成传感器,高频数据用环形缓冲区 + 节流检测,后台场景用批处理或触发器。
下篇讲生物认证,BiometricPrompt 不只是指纹,还有面部和硬件密钥。