如何获取加速度计、陀螺仪和磁力计的数据?它们的单位是什么?
解读
国内面试中,这道题考察的是“传感器框架”这一基础但高频的模块。面试官想确认三件事:
- 你是否真的在真机上用过 SensorManager,而不是只看过文档;
- 你是否理解三种物理量的工程含义,避免把“加速度 9.8”当成异常值;
- 你是否具备“采样率+功耗”意识,因为国内厂商对后台功耗极度敏感,直接决定你的代码能否过 CTS 和厂商功耗测试。
因此,回答时要给出“注册—采样—换算—释放”完整闭环,并主动把单位、量程、坐标系、采样率、功耗、后台限制这些点一次讲清,体现“工程落地”经验。
知识点
-
SensorManager 获取与注册:
getSystemService(Context.SENSOR_SERVICE)→registerListener(listener, sensor, rateUs, handler)- 采样率等级:SENSOR_DELAY_FASTEST / GAME / UI / NORMAL,对应 0 μs、20 000 μs、60 000 μs、200 000 μs,实际值由 HAL 层和厂商配置决定。
-
三种传感器类型:
TYPE_ACCELEROMETER:硬件加速度计,单位 m/s²,含重力分量。TYPE_GYROSCOPE:硬件陀螺仪,单位 rad/s,逆时针为正,遵循右手定则。TYPE_MAGNETIC_FIELD:硬件磁力计,单位 μT(微特斯拉),1 μT = 0.01 G(高斯)。
-
事件回调:
onSensorChanged(SensorEvent event):values[0~2] 分别对应 X/Y/Z 轴,坐标系与手机自然方向绑定,与屏幕旋转无关。onAccuracyChanged(Sensor sensor, int accuracy):ACCURACY_HIGH / MEDIUM / LOW / UNRELIABLE,国内部分机型在金属环境会频繁回调 UNRELIABLE,需做降级处理。
-
后台限制(国内重点):
- Android 9 以后后台应用无法接收 ≥200 Hz 连续采样;
- 部分厂商(华为、OPPO)在息屏 5 min 后强制降频到 5 Hz,甚至直接 unregister;
- 若需高频率实时数据,必须前台 Service + 可见通知,否则会被系统或管家杀掉。
-
资源释放:
- 在
onPause()/onDestroy()中必须unregisterListener,否则会被标记为“传感器泄露”,导致应用耗电异常告警,无法上架国内商店。
- 在
-
单位换算与校准:
- 加速度:event.values[i] 直接就是 m/s²,重力分量 9.8 无需再乘;
- 陀螺仪:rad/s 转 deg/s 乘 180/π;
- 磁力计:μT 转 mT 乘 0.001,国内地图 SDK 一般要求 μT 级输入。
答案
第一步,获取系统服务并检查硬件是否存在:
val sm = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val acc = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
val gyr = sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
val mag = sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
if (acc == null || gyr == null || mag == null) {
// 国内低端机可能缺陀螺仪,需降级或提示用户
}
第二步,定义监听并注册,采样率根据业务选择:
val listener = object : SensorEventListener {
override fun onSensorChanged(e: SensorEvent) {
when (e.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> {
val x = e.values[0] // m/s²
val y = e.values[1]
val z = e.values[2]
}
Sensor.TYPE_GYROSCOPE -> {
val wx = e.values[0] // rad/s
val wy = e.values[1]
val wz = e.values[2]
}
Sensor.TYPE_MAGNETIC_FIELD -> {
val bx = e.values[0] // μT
val by = e.values[1]
val bz = e.values[2]
}
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
if (accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
// 国内金属支架场景极易触发,需提示用户校准
}
}
}
// 注册:前台 UI 线程用 GAME 级别,后台高频率任务用 FASTEST + 前台 Service
sm.registerListener(listener, acc, SensorManager.SENSOR_DELAY_GAME)
sm.registerListener(listener, gyr, SensorManager.SENSOR_DELAY_GAME)
sm.registerListener(listener, mag, SensorManager.SENSOR_DELAY_GAME)
第三步,在生命周期结束时必须释放:
override fun onPause() {
super.onPause()
sm.unregisterListener(listener)
}
单位总结:
加速度计:m/s²(含重力 9.8);
陀螺仪:rad/s;
磁力计:μT。
拓展思考
-
虚拟传感器与融合:
国内很多入门机砍掉陀螺仪,但系统仍提供TYPE_GRAVITY、TYPE_LINEAR_ACCELERATION、TYPE_ROTATION_VECTOR,底层通过加速度+磁力计+算法融合生成。面试时可主动说明“若硬件缺失,可降级到 ROTATION_VECTOR,但动态精度下降 30%,需业务侧容忍漂移”。 -
功耗实战:
连续 200 Hz 采样在 4000 mAh 手机上每小时耗电约 6%,国内商店审核要求“后台传感器任务必须≤5 Hz”。可采取“退至后台自动降级 + 显著性检测(方差阈值)停采”策略,既保活又省电。 -
坐标系对齐:
国内折叠屏和 Pad 越来越多,默认传感器坐标系基于自然方向,与屏幕旋转无关。若业务需要“屏幕坐标系”,必须结合Display.getRotation()做 4 组映射矩阵切换,否则会出现“横屏下左右颠倒”的客诉。 -
安全与隐私:
2023 年起,国内主流厂商在隐私合规检测中把“传感器列表读取”列为高风险权限,需在隐私政策中声明用途。若用于摇一摇广告,还必须提供开关,否则无法通过华为、小米、OPPO 的上架扫描。