5、注册传感器监听:registerListener 方法详解,包括参数说明

好,咱们接着往下走。上一章我们把传感器对象拿到手了,但光拿到对象没用,你得告诉系统:「嘿,我要开始监听陀螺仪的数据了!」。这个动作,就是通过 registerListener 来完成的。

说白了,这个方法就是给传感器装上一个「耳朵」,让它一有数据变化就通知你。我刚开始学的时候,以为注册了就完事了,结果发现参数没填对,回调死活不触发……嗯,这里面的门道,咱们今天一次说清楚。

5.1 方法签名与重载形式

SensorManager 提供了好几个重载的 registerListener,但最常用的就两种:

// 方式一:最基础版
boolean registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs);

// 方式二:带 Handler 版
boolean registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs, Handler handler);

返回值是个 boolean,告诉你注册是否成功。我在项目中遇到过,某些低端机型或者模拟器上,注册会返回 false,这时候你得做降级处理,不能直接崩掉。

5.2 三个核心参数详解

咱们一个一个拆开看,每个参数都有坑。

参数一:listener —— 你的回调接口

这个就是 SensorEventListener 接口的实现对象。它有两个方法必须重写:

  • onSensorChanged(SensorEvent event) —— 传感器数据变化时触发,这是你拿数据的地方
  • onAccuracyChanged(Sensor sensor, int accuracy) —— 传感器精度变化时触发,比如从高精度降到了低精度

我个人习惯把 listener 写成匿名内部类或者直接在 Activity/Fragment 里实现接口。但要注意一点:不要每次注册都 new 一个新的 listener 对象,否则取消注册时可能对不上号。

小技巧: 我一般会在类的成员变量里声明一个 listener 引用,注册和反注册都用同一个对象,避免「注册了但取消不掉」的尴尬。

参数二:sensor —— 你要监听哪个传感器

这个就是上一章我们用 getDefaultSensor() 拿到的 Sensor 对象。你传什么传感器,就监听什么数据。传陀螺仪就收角速度,传加速度计就收加速度。

这里有个容易犯的错:不要传 TYPE_ALL 或者直接传个 int 常量。必须传 Sensor 对象实例。我曾经见过有人这么写:

// ❌ 错误写法
sensorManager.registerListener(listener, Sensor.TYPE_GYROSCOPE, ...);

// ✅ 正确写法
Sensor gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
sensorManager.registerListener(listener, gyro, ...);

为什么会这样?因为 registerListener 第二个参数要求的是 Sensor 类型,不是 int。编译器会报错,但有些人硬要强转……嗯,别这么干。

参数三:samplingPeriodUs —— 采样频率

这个参数的单位是微秒(μs),1秒 = 1,000,000 微秒。它决定了系统多久给你回调一次数据。

Android 系统预定义了四个常量,方便你直接使用:

常量名 值(微秒) 约合频率 适用场景
SENSOR_DELAY_NORMAL 200,000 5 Hz 屏幕方向、简单游戏
SENSOR_DELAY_UI 60,000 16.7 Hz UI 更新、动画
SENSOR_DELAY_GAME 20,000 50 Hz 游戏、实时交互
SENSOR_DELAY_FASTEST 0 最快 高频采集、专业应用
警告: 别一上来就用 SENSOR_DELAY_FASTEST!你以为「越快越好」,实际上它会疯狂消耗 CPU 和电池。我做过测试,FASTEST 模式下,某些机型每秒回调上千次,手机瞬间发烫。除非你真的需要那么高的频率,否则用 SENSOR_DELAY_GAME 或者 SENSOR_DELAY_UI 就足够了。

另外,你传的值只是一个「建议值」,系统不一定会完全按照这个频率来。实际频率取决于硬件能力和系统负载。你想想看,如果硬件最高只支持 100 Hz,你传个 1,000 Hz 也没用。

5.3 带 Handler 的版本

第二个重载版本多了一个 Handler 参数。这个参数的作用是:指定回调在哪个线程执行

Handler myHandler = new Handler(Looper.getMainLooper());
sensorManager.registerListener(listener, gyro, SENSOR_DELAY_GAME, myHandler);

如果你不传 Handler,回调默认在「传感器事件分发线程」里执行,这个线程不是主线程。所以如果你要在回调里更新 UI,要么自己 post 到主线程,要么直接传一个绑定主线程 Looper 的 Handler。

我个人习惯:只要涉及 UI 更新,就传主线程的 Handler。省得后面还要写 runOnUiThread,代码更清爽。

5.4 注册流程的完整代码

把上面这些串起来,一个标准的注册流程长这样:

public class MainActivity extends AppCompatActivity implements SensorEventListener {

    private SensorManager sensorManager;
    private Sensor gyroSensor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

        if (gyroSensor == null) {
            // 没有陀螺仪,做降级处理
            return;
        }

        // 注册监听,使用 GAME 频率,回调在主线程
        sensorManager.registerListener(
            this,
            gyroSensor,
            SensorManager.SENSOR_DELAY_GAME,
            new Handler(Looper.getMainLooper())
        );
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // 这里直接更新 UI,因为已经在主线程了
        float x = event.values[0];
        float y = event.values[1];
        float z = event.values[2];
        // 更新界面...
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 精度变化时处理
    }
}

5.5 知识体系图

下面这张图帮你理清 registerListener 的核心逻辑:

registerListener 核心流程 registerListener 调用 参数1:listener SensorEventListener 接口 参数2:sensor Sensor 对象实例 参数3:采样频率 微秒单位 onSensorChanged() onAccuracyChanged() 必须实现这两个方法 getDefaultSensor() TYPE_GYROSCOPE 先获取再注册 NORMAL / UI / GAME / FASTEST 建议值,非强制 频率越高越耗电 返回 boolean:成功/失败

5.6 避坑指南

最后,分享几个我踩过的坑:

  • 重复注册问题: 如果你在 onResume() 里注册,别忘了在 onPause() 里取消注册。否则 Activity 切到后台再回来,会注册多个 listener,回调乱成一锅粥。
  • 空指针检查: getDefaultSensor() 可能返回 null,尤其是模拟器或者没有陀螺仪的设备。注册前一定要判空。
  • 频率别写死: 我曾经在代码里直接写 10000(10毫秒),以为这样能拿到最高频数据。结果某些老机型直接不回调了,因为硬件根本跑不到那么快。用系统预定义的常量更安全。

嗯,registerListener 的内容就这么多。参数不多,但每个都有讲究。下一章咱们聊聊反注册,也就是什么时候该把耳朵关掉。


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