6、注册与注销监听器:registerListener() 参数详解
光线传感器要真正工作起来,关键一步就是注册监听器。这一步看似简单,但坑不少。我见过不少新手在这里翻车——要么忘记注销导致内存泄漏,要么采样率设置不合理把电池耗光。
今天我们就来把 registerListener() 的三个参数彻底讲透,再聊聊 onPause/onResume 里的最佳实践。
6.1 registerListener() 的三个参数
先看方法签名:
SensorManager.registerListener(
SensorEventListener listener,
Sensor sensor,
int samplingPeriodUs
)
三个参数,一个都不能错。我们逐个拆解。
参数一:SensorEventListener
这是个接口,你需要实现两个回调方法:
onSensorChanged(SensorEvent event)—— 传感器数据变化时触发onAccuracyChanged(Sensor sensor, int accuracy)—— 精度变化时触发
我个人习惯把 SensorEventListener 直接写在 Activity 或 Fragment 里实现,简单粗暴。但如果你有多个传感器要监听,建议单独抽一个类出来,不然代码会变得很臃肿。
onAccuracyChanged 里可以什么都不写。但别空着,打个日志也好,方便调试。
参数二:Sensor 对象
这个就是你之前通过 getDefaultSensor() 拿到的传感器实例。注意一点:不要每次注册时都重新获取 Sensor 对象。我建议在 onCreate 里一次性获取并保存起来,反复使用。
// 推荐做法
private Sensor mLightSensor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
mLightSensor = sm.getDefaultSensor(Sensor.TYPE_LIGHT);
}
参数三:采样率(samplingPeriodUs)
这个参数最容易让人困惑。它决定了系统多久给你回调一次数据。单位是微秒,但实际开发中我们通常用 SensorManager 提供的常量:
| 常量 | 含义 | 典型延迟 | 适用场景 |
|---|---|---|---|
SENSOR_DELAY_NORMAL |
普通延迟 | 约 200ms | 屏幕亮度自动调节 |
SENSOR_DELAY_UI |
UI 更新频率 | 约 60ms | 实时显示光照值 |
SENSOR_DELAY_GAME |
游戏级延迟 | 约 20ms | 快速响应场景 |
SENSOR_DELAY_FASTEST |
最快频率 | 约 5ms | 极少使用,耗电高 |
NORMAL,但系统可能给你 300ms 一次。别太较真。
我曾经在一个项目里用了 FASTEST 来监听光线传感器,结果手机发烫严重。后来发现光线变化没那么快,改成 UI 就完全够用了。说白了,采样率不是越高越好,够用就行。
6.2 注销监听器:unregisterListener()
注册了就得注销,这是铁律。不注销会怎样?
- Activity 销毁了,但 SensorManager 还持有你的回调引用
- GC 无法回收你的 Activity,造成内存泄漏
- 传感器持续工作,白白消耗电量
注销代码很简单:
sensorManager.unregisterListener(mLightSensorListener);
嗯,这里要注意:注销时传入的 listener 对象必须和注册时是同一个。如果你注册时 new 了一个匿名内部类,注销时又 new 了一个,那就注销不掉了。我刚开始就犯过这个错,排查了半天。
6.3 onPause/onResume 中的最佳实践
这是整个章节最核心的部分。为什么要在 onPause 和 onResume 里做注册注销?
你想想看,用户按了 Home 键,Activity 进入后台。如果传感器还在工作,那就是在浪费资源。等用户切回来,再重新注册。这样既省电又安全。
标准写法如下:
@Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(
mLightSensorListener,
mLightSensor,
SensorManager.SENSOR_DELAY_UI
);
}
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(mLightSensorListener);
}
- 在
onResume中注册监听器 - 在
onPause中注销监听器 - 不要在
onDestroy中注销,因为onDestroy不一定被调用 - 如果使用了多个传感器,统一管理注册注销逻辑
我曾经接手过一个项目,代码里在 onStart 注册、onStop 注销。看起来没问题对吧?但那个 App 有个画中画模式,onStart/onStop 的调用时机和 onResume/onPause 不一样,导致画中画模式下传感器还在跑。后来全部改成 onResume/onPause 就解决了。
6.4 知识体系总览
下面这张图帮你理清注册注销的整体流程:
6.5 避坑指南
最后分享几个我踩过的坑:
- 重复注册:如果
onResume被多次调用,记得先注销再注册,或者加个标志位判断。 - 空指针:如果设备没有光线传感器,
getDefaultSensor()返回 null。注册前一定要判空。 - Context 泄漏:如果你在 Fragment 里注册,记得用
getActivity()获取 Context,不要持有 Activity 的强引用。
嗯,关于注册注销就讲这么多。记住一个口诀:成对出现,生命周期对齐,采样率够用就好。做到这三点,你的传感器代码基本就稳了。
公众号:蓝海资料掘金营,微信deep3321