6、注册与注销监听器:registerListener与unregisterListener的正确用法、参数详解

好,咱们接着聊距离传感器的核心操作——注册和注销监听器。

说白了,你写了个App想监听距离传感器,总得告诉系统一声:“嘿,我要开始听了!” 这就是registerListener干的事。等你不听了,也得说一声:“行了,我撤了。” 这就是unregisterListener

这两兄弟用不对,轻则内存泄漏,重则App直接崩掉。我早期做项目时,就吃过这个亏。

6.1 registerListener:告诉系统“我要监听”

先看最常用的方法签名:

boolean registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs)

返回一个boolean值。true表示注册成功,false表示失败。我建议你每次调用后都检查一下这个返回值,别想当然。

参数详解

参数 类型 说明
listener SensorEventListener 你的监听器对象,负责接收传感器事件
sensor Sensor 你要监听的传感器实例,从SensorManager获取
samplingPeriodUs int 采样周期,单位是微秒(μs)

这里重点说说samplingPeriodUs。你可能会问:“我直接传个1000000(1秒)不就行了?” 嗯,没那么简单。

samplingPeriodUs 的四个预定义常量

Android系统贴心地给了四个常量,你直接用就行:

  • SENSOR_DELAY_NORMAL(200000μs = 200ms)—— 适合普通场景,比如屏幕旋转
  • SENSOR_DELAY_UI(60000μs = 60ms)—— 适合UI更新,比如游戏界面
  • SENSOR_DELAY_GAME(20000μs = 20ms)—— 适合游戏,要求响应快
  • SENSOR_DELAY_FASTEST(0μs)—— 最快速度,不保证稳定

我个人习惯,做距离传感器相关功能时,用SENSOR_DELAY_UI就够了。为什么?因为距离传感器本身更新频率不高,你没必要用FASTEST去浪费CPU。

核心要点:采样周期越短,CPU唤醒越频繁,耗电越快。别为了“快”而“快”。

一个完整的注册示例

SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);

if (proximitySensor != null) {
    boolean registered = sensorManager.registerListener(
        this,                    // 假设Activity实现了SensorEventListener
        proximitySensor,
        SensorManager.SENSOR_DELAY_UI
    );
    if (!registered) {
        // 注册失败,可能是传感器不可用
        Log.w("ProximitySensor", "注册失败");
    }
}

你看,代码很简单。但有个坑——注册前一定要判空。有些设备可能没有距离传感器,直接注册会抛NullPointerException。我曾经在一个平板上遇到过,那平板确实没有距离传感器,结果App直接闪退。从那以后,我每次都会先判空。

6.2 unregisterListener:用完记得“注销”

有注册就有注销。方法签名更简单:

void unregisterListener(SensorEventListener listener)

只需要传一个监听器对象。系统会自动解除该监听器绑定的所有传感器。

你可能会想:“我不注销会怎样?” 嗯,后果很严重。

  • 内存泄漏:Activity销毁了,但监听器还在,系统会一直持有你的Activity引用,GC回收不了
  • 电量浪费:传感器一直开着,CPU不断被唤醒,耗电快
  • 事件泄露:已经销毁的页面还在接收传感器事件,可能引发空指针

警告:忘记调用unregisterListener是Android开发中最常见的内存泄漏原因之一。我见过不少项目,就是因为这个导致OOM。

正确的注销时机

我建议在onPause()onStop()中注销。为什么不是onDestroy()?因为onDestroy()不一定被调用(比如系统直接杀进程)。

@Override
protected void onPause() {
    super.onPause();
    if (sensorManager != null) {
        sensorManager.unregisterListener(this);
    }
}

注意:注销前也要判空,避免空指针。

6.3 注册与注销的完整生命周期

我画了一张图,帮你理清整个流程:

注册与注销监听器生命周期 Activity onCreate registerListener 传入listener、sensor、samplingPeriodUs 监听中(onSensorChanged) 持续接收距离传感器数据 unregisterListener 在onPause或onStop中调用 Activity onDestroy

这张图很直观:registerListeneronCreate之后调用,unregisterListeneronPauseonStop中调用,最后onDestroy时确保已经注销。

6.4 常见错误与避坑指南

技巧:如果你在多个地方注册了同一个监听器,只需要调用一次unregisterListener就能全部解除。系统会帮你清理干净。

下面是我总结的几个常见错误:

  • 重复注册:同一个监听器注册多次,会导致onSensorChanged被多次调用。我曾经在onResumeonStart里都注册了,结果数据翻倍,排查了半天才发现。
  • 忘记注销:这个前面说过了,内存泄漏的元凶。
  • 在子线程注册registerListener必须在主线程调用,否则会抛异常。你想想看,传感器事件回调也是在主线程,你非要在子线程注册,系统怎么处理?
  • 传了null的listener:会直接抛IllegalArgumentException。所以传参前一定要确保listener不为null。

注意:如果你在onPause中注销了监听器,记得在onResume中重新注册。否则App从后台切回来时,传感器就不工作了。

6.5 最佳实践总结

说了这么多,我总结几条铁律:

  1. 注册前判空:传感器可能不存在
  2. 选择合适的采样周期:别盲目追求快
  3. 在onPause中注销:别等到onDestroy
  4. 在onResume中重新注册:保证前后台切换正常
  5. 检查注册返回值:false就说明有问题

嗯,这些就是我多年实战中总结出来的经验。你按照这个来,基本不会出大问题。

好了,注册与注销这块就聊到这儿。记住:有借有还,再借不难。注册了就要注销,这是Android开发的基本素养。


公众号:蓝海资料掘金营,微信deep3321