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 对象,否则取消注册时可能对不上号。
参数二: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 的核心逻辑:
5.6 避坑指南
最后,分享几个我踩过的坑:
- 重复注册问题: 如果你在
onResume()里注册,别忘了在onPause()里取消注册。否则 Activity 切到后台再回来,会注册多个 listener,回调乱成一锅粥。 - 空指针检查:
getDefaultSensor()可能返回 null,尤其是模拟器或者没有陀螺仪的设备。注册前一定要判空。 - 频率别写死: 我曾经在代码里直接写
10000(10毫秒),以为这样能拿到最高频数据。结果某些老机型直接不回调了,因为硬件根本跑不到那么快。用系统预定义的常量更安全。
嗯,registerListener 的内容就这么多。参数不多,但每个都有讲究。下一章咱们聊聊反注册,也就是什么时候该把耳朵关掉。