3、监听加速度数据:注册与注销 SensorEventListener

好,现在传感器拿到了,也确认它活着了。下一步干嘛?

监听数据。

说白了,就是让系统在加速度变化时主动通知你。你不能自己写个死循环去轮询——那太傻了,而且耗电。Android 提供了 SensorEventListener 这个接口,你实现它,然后注册给传感器管理器。数据一来,系统回调你。

3.1 实现 SensorEventListener 接口

这个接口只有两个方法需要实现:

  • onSensorChanged(SensorEvent event) —— 传感器数值变化时触发
  • onAccuracyChanged(Sensor sensor, int accuracy) —— 传感器精度变化时触发

我个人的习惯是,把监听器写成内部类或者直接在 Activity/Fragment 里实现接口。简单项目我直接让 Activity 实现,方便管理上下文。

public class MainActivity extends AppCompatActivity implements SensorEventListener {

    private SensorManager sensorManager;
    private Sensor accelerometer;

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

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];

            // 这里处理你的业务逻辑
            // 比如更新UI、判断摇一摇、记录数据等
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 精度变化时回调,一般不太常用
        // accuracy 取值:SENSOR_STATUS_UNRELIABLE, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH
    }
}

嗯,这里要注意:onSensorChanged 是在 子线程 里被调用的。你不能在里面直接更新 UI,否则会崩溃。我早期踩过这个坑,一运行就闪退,找了半天才发现是线程问题。

⚠️ 重要提醒:
onSensorChanged 运行在子线程。更新 UI 请使用 Handler、runOnUiThread 或 View.post()。

3.2 注册监听器

实现完接口,接下来就是注册。一般在 onResume() 里注册,因为这时候 Activity 可见,用户正在使用,传感器数据才有意义。

@Override
protected void onResume() {
    super.onResume();
    if (accelerometer != null) {
        sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    }
}

第三个参数是采样频率,Android 提供了四个级别:

常量 说明 典型延迟
SENSOR_DELAY_NORMAL 适合普通场景,比如屏幕旋转 约 200ms
SENSOR_DELAY_UI 适合更新 UI,比如游戏 约 60ms
SENSOR_DELAY_GAME 适合游戏,响应快 约 20ms
SENSOR_DELAY_FASTEST 最快频率,耗电高 约 5ms

你想想看,如果只是判断手机是否平放,用 NORMAL 就够了。要是做体感游戏,那得用 GAME 甚至 FASTEST。我在做一个跑步 App 时,用了 FASTEST,结果手机半小时就没电了……后来改成 UI 级别,完全够用。

💡 我的建议:
不要盲目追求高频。频率越高,CPU 唤醒越频繁,耗电越严重。先想清楚你的业务需要多快的响应,再选合适的延迟。

3.3 注销监听器

这个步骤很多人会忘。但忘了的后果很严重——传感器会一直工作,即使 App 退到后台。电量就是这么悄悄溜走的。

我一般会在 onPause() 里注销。为什么不是 onStop()?因为 onPause() 更早触发,能更快释放资源。

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

注意:unregisterListener 会注销当前 Activity 注册的 所有 传感器监听。如果你只注册了加速度计,那没问题。如果注册了多个传感器,只想注销其中一个,可以传第二个参数指定 Sensor 对象:

sensorManager.unregisterListener(this, accelerometer);
⚠️ 曾经踩过的坑:
我曾经在 onDestroy() 里注销,结果发现用户按 Home 键回到桌面,传感器还在跑。因为 onDestroy() 不一定立即调用。所以记住:在 onPause() 里注销,这是最佳实践。

3.4 完整生命周期管理

把注册和注销放到生命周期里,形成一个闭环:

@Override
protected void onResume() {
    super.onResume();
    sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
}

@Override
protected void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
}

这样,用户切到后台时,传感器停止工作。用户切回来时,重新注册。既省电又安全。

3.5 核心逻辑流程图

下面这张图,帮你理清整个注册与注销的流程:

SensorEventListener 注册与注销流程 Activity 启动 onResume() registerListener() onSensorChanged() 用户离开 App onPause() unregisterListener() 注册在 onResume,注销在 onPause,形成完整生命周期闭环

3.6 常见问题与避坑

  • 忘记注销:传感器持续工作,耗电飙升。我见过一个 App 后台跑了一整夜,手机直接没电关机。
  • 在 onDestroy 里注销:不靠谱。按 Home 键不会立即触发 onDestroy,传感器会一直运行。
  • 多次注册:同一个 Listener 多次注册,会导致回调多次触发。每次 onResume 只注册一次就好。
  • 空指针:如果 getDefaultSensor 返回 null,说明设备没有加速度传感器。注册前一定要判空。
✅ 最佳实践总结:
  1. 在 onResume() 里注册监听器
  2. 在 onPause() 里注销监听器
  3. onSensorChanged 里不要直接更新 UI
  4. 根据业务需求选择合适的采样频率
  5. 记得判空,防止无传感器设备崩溃

好了,注册与注销就这些。说白了就是两行代码的事,但放对位置很重要。你想想看,一个 App 里几十个页面,每个都忘了注销,那用户的电量可就遭殃了。

我早期做第一个传感器项目时,就犯过这个错。测试的时候没感觉,上线后用户反馈手机发烫、掉电快。查了半天,发现是传感器没注销。从那以后,我每次写传感器代码,第一件事就是把 onResume 和 onPause 的配对写好。


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