4. 注册与注销监听:registerListener与unregisterListener的正确使用时机
好,咱们接着聊。上一节我们把 SensorManager 和 Sensor 对象都拿到手了,接下来就是最关键的一步——让传感器真正工作起来。
说白了,registerListener 就是告诉系统:「嘿,我要开始监听这个传感器了」。而 unregisterListener 则是:「好了,我不听了,你歇着吧」。听起来简单?嗯,但我在项目里见过太多人在这两个 API 上翻车。
4.1 registerListener 的三种重载
先看看官方给我们提供了哪几种注册方式。我个人习惯把它们分成三类:
| 方法签名 | 采样率参数 | 适用场景 |
|---|---|---|
| registerListener(SensorEventListener, Sensor, int) | 延迟级别(SENSOR_DELAY_*) | 大多数普通场景 |
| registerListener(SensorEventListener, Sensor, int, Handler) | 延迟级别 + 指定 Handler | 需要在特定线程处理回调 |
| registerListener(SensorEventListener, Sensor, int, int) | 延迟级别 + 最大报告延迟(微秒) | 需要批量处理传感器事件 |
你可能会问:「延迟级别到底选哪个?」我直接给你一个经验值:
- SENSOR_DELAY_NORMAL(200,000 微秒):屏幕方向、环境光这类变化不频繁的场景。省电。
- SENSOR_DELAY_UI(60,000 微秒):UI 交互需要,比如根据倾斜角度做动画。我在做体感游戏时常用这个。
- SENSOR_DELAY_GAME(20,000 微秒):游戏、实时姿态检测。耗电明显增加。
- SENSOR_DELAY_FASTEST(0 微秒):能多快就多快。除非你真的需要毫秒级响应,否则别碰它。我曾经在一个项目里用了 FASTEST,结果手机发烫到能煎鸡蛋。
核心原则:采样率够用就行,别贪快。霍尔传感器通常用 NORMAL 或 UI 就足够了。
4.2 在 Activity 生命周期中管理传感器
这是本章的重头戏。很多新手会把 registerListener 写在 onCreate 里,然后忘了注销。结果呢?Activity 销毁了,传感器还在后台跑,电量哗哗地掉。
正确的做法是:在 onResume 中注册,在 onPause 中注销。
为什么是 onPause 而不是 onDestroy?因为 onPause 一定会被调用,而 onDestroy 不一定。比如用户按了 Home 键,Activity 只是暂停,但传感器应该停止监听——你想想看,用户都切出去了,你还开着传感器干嘛?
public class HallSensorActivity extends AppCompatActivity {
private SensorManager sensorManager;
private Sensor hallSensor;
private SensorEventListener hallListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hall);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
hallSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
// 初始化监听器
hallListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
// 处理霍尔数据
float x = event.values[0];
// 更新UI或其他逻辑
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 精度变化时处理
}
};
}
@Override
protected void onResume() {
super.onResume();
if (hallSensor != null) {
sensorManager.registerListener(hallListener, hallSensor,
SensorManager.SENSOR_DELAY_NORMAL);
}
}
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(hallListener);
}
}
我的习惯:在 onPause 里注销后,我会把监听器置为 null 吗?不会。因为 onResume 里还要用。但如果你在 onDestroy 里也做一次 unregisterListener,也没坏处——系统会忽略重复注销。
4.3 注销的坑:我曾经踩过的
讲个真实案例。有一次我做智能保护壳项目,霍尔传感器用来检测翻盖状态。我在 onStop 里注销了监听器,结果发现每次从后台切回来,传感器要等好几秒才有反应。
排查了半天,原因是什么?onStop 和 onPause 的调用时机不同。onPause 之后 Activity 可能还可见(比如对话框弹出),但 onStop 之后 Activity 就完全不可见了。如果你在 onStop 才注销,那用户切到另一个 App 再回来,中间这段时间传感器其实还在跑。
所以记住:注销要早,注册要晚。onPause 注销,onResume 注册,这是黄金法则。
特别注意:如果你在多个地方注册了同一个监听器,系统会保留最后一次注册的配置。注销时只需要调用一次 unregisterListener,系统会帮你清理所有关联。
4.4 知识体系:生命周期与传感器管理
下面这张图是我自己整理的,把 Activity 生命周期和传感器注册注销的对应关系画清楚了。你一看就明白。
4.5 多传感器场景下的管理
如果你的 Activity 需要同时监听多个传感器(比如霍尔 + 加速度计),我建议你用一个统一的监听器对象,在 onSensorChanged 里通过 sensor.getType() 区分。
private SensorEventListener multiListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_MAGNETIC_FIELD:
// 处理霍尔数据
break;
case Sensor.TYPE_ACCELEROMETER:
// 处理加速度数据
break;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 统一处理精度变化
}
};
这样做的好处是:注销时只需要一行代码。你想想看,如果每个传感器都单独搞一个监听器,那注销的时候得写好几行,还容易漏掉。
最佳实践总结:
- onResume 注册,onPause 注销——雷打不动
- 一个监听器处理多个传感器——简洁高效
- 采样率选 NORMAL 或 UI——省电够用
- 注销前检查 sensorManager 是否为 null——防止空指针
好了,这一节的内容就这些。记住:注册和注销不是小事,它直接关系到你的 App 耗电和稳定性。我在代码审查时,第一眼就看生命周期里有没有正确处理传感器。这个习惯,建议你也养成。