3、第一个传感器应用:注册与注销传感器监听、onSensorChanged回调机制、处理传感器精度变化
好,咱们今天来写第一个真正的传感器应用。
说实话,很多新手一上来就急着看陀螺仪、加速度计的数据,结果代码写完了,传感器没反应。为什么?因为忘了注册监听。这就像你打电话给别人,号码拨了但没按拨号键——永远打不通。
3.1 注册传感器监听——让系统知道你要什么
注册监听,说白了就是告诉系统:「嘿,我对某个传感器的数据感兴趣,有更新了通知我。」
核心代码就一行:
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
这里有几个关键点,我一个个说。
3.1.1 三个参数的含义
| 参数 | 类型 | 说明 |
|---|---|---|
| listener | SensorEventListener | 谁负责接收回调?通常是 Activity 或 Fragment 实现接口 |
| sensor | Sensor | 你要监听哪个传感器?从 getDefaultSensor 获取 |
| samplingPeriodUs | int | 数据采样频率,单位微秒。有四个预设值 |
采样频率这块,我踩过坑。曾经有个项目,做计步器功能,我用了 SENSOR_DELAY_FASTEST,结果手机发烫、电量狂掉。后来改成 SENSOR_DELAY_NORMAL,数据完全够用。
四种采样频率对比
SENSOR_DELAY_FASTEST—— 最快,约 0ms 延迟。适合游戏、实时绘图SENSOR_DELAY_GAME—— 较快,约 20ms。适合需要快速响应的场景SENSOR_DELAY_UI—— 适中,约 60ms。适合更新 UISENSOR_DELAY_NORMAL—— 默认,约 200ms。适合大多数场景
我的建议:除非你真的需要毫秒级响应,否则别用 FASTEST。电池扛不住,用户会骂的。
3.2 onSensorChanged 回调——数据来了
注册完监听,系统就会在传感器数据变化时调用 onSensorChanged。这个方法是整个传感器编程的核心。
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
// 更新 UI 或做逻辑处理
}
}
SensorEvent 里最重要的就是 values 数组。不同传感器,数组长度和含义不一样:
- 加速度计:values[0] = X轴,values[1] = Y轴,values[2] = Z轴,单位 m/s²
- 陀螺仪:values[0~2] = 绕 X/Y/Z 轴的角速度,单位 rad/s
- 光线传感器:values[0] = 光照强度,单位 lux
嗯,这里要注意:onSensorChanged 是在 子线程 调用的。你不能在里面直接更新 UI,否则会崩溃。我刚开始做的时候就被坑过——直接往 TextView 里 setText,结果闪退。正确做法是用 Handler 或者 runOnUiThread。
重要提醒:onSensorChanged 的调用频率取决于你注册时设置的采样周期。如果你用 FASTEST,这个方法可能每秒被调用几百次。不要在回调里做耗时操作,比如写文件、网络请求。否则 UI 线程会被卡死。
3.3 处理传感器精度变化
还有一个回调方法,很多人会忽略:onAccuracyChanged。
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
switch (accuracy) {
case SensorManager.SENSOR_STATUS_UNRELIABLE:
// 数据不可靠,建议提示用户
break;
case SensorManager.SENSOR_STATUS_ACCURACY_LOW:
// 精度低,但还能用
break;
case SensorManager.SENSOR_STATUS_MEDIUM:
// 中等精度
break;
case SensorManager.SENSOR_STATUS_HIGH:
// 高精度,放心用
break;
}
}
这个回调什么时候触发?比如你拿着手机从室内走到室外,光线传感器精度会变化。或者陀螺仪需要校准的时候。我曾经在做一个 AR 导航项目时,发现陀螺仪数据突然飘了,就是因为精度从 HIGH 降到了 LOW。当时没处理这个回调,结果导航箭头乱转,用户差点投诉。
经验之谈:精度变化回调里,至少要对 SENSOR_STATUS_UNRELIABLE 做处理。可以弹个 Toast 提示用户「传感器异常,请重启应用」。别让用户莫名其妙。
3.4 注销监听——别忘了收尾
注册了监听,就一定要注销。否则 Activity 销毁了,传感器还在跑,电池继续耗。这是新手最容易犯的错误。
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
为什么在 onPause 注销而不是 onDestroy?因为用户按 Home 键回到桌面时,onPause 会执行,但 onDestroy 不一定。如果你在 onDestroy 才注销,用户切到桌面后传感器还在工作,白白耗电。
血的教训:我曾经有个版本忘记在 onPause 注销监听,结果用户反馈手机待机一晚上掉电 30%。查了半天,发现是加速度计一直在跑。从那以后,我每次写传感器代码,第一件事就是把注册和注销的成对代码写好,再写业务逻辑。
3.5 完整代码骨架
把上面这些串起来,就是一个完整的传感器应用骨架:
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private SensorManager sensorManager;
private Sensor accelerometer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
@Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
// 在这里处理数据
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 处理精度变化
}
}
这个骨架,你直接复制就能跑。但别急着复制,先理解每一行在干什么。
3.6 本章知识体系
我把整个流程画了张图,方便你理解:
这张图把整个流程串起来了。你写代码的时候,就按这个顺序来:获取管理器 → 获取传感器 → 注册 → 处理回调 → 注销。少一步都不行。
好了,第一个传感器应用的核心内容就这些。代码不多,但每一步都有讲究。你动手写一遍,跑起来,再试试不同采样频率的效果,感受一下数据的变化。实践出真知。
公众号:蓝海资料掘金营,微信deep3321