3、第一个传感器应用:注册与注销传感器监听、onSensorChanged回调机制、处理传感器精度变化

好,咱们今天来写第一个真正的传感器应用。

说实话,很多新手一上来就急着看陀螺仪、加速度计的数据,结果代码写完了,传感器没反应。为什么?因为忘了注册监听。这就像你打电话给别人,号码拨了但没按拨号键——永远打不通。

3.1 注册传感器监听——让系统知道你要什么

注册监听,说白了就是告诉系统:「嘿,我对某个传感器的数据感兴趣,有更新了通知我。」

核心代码就一行:

SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);

这里有几个关键点,我一个个说。

3.1.1 三个参数的含义

参数 类型 说明
listener SensorEventListener 谁负责接收回调?通常是 Activity 或 Fragment 实现接口
sensor Sensor 你要监听哪个传感器?从 getDefaultSensor 获取
samplingPeriodUs int 数据采样频率,单位微秒。有四个预设值

采样频率这块,我踩过坑。曾经有个项目,做计步器功能,我用了 SENSOR_DELAY_FASTEST,结果手机发烫、电量狂掉。后来改成 SENSOR_DELAY_NORMAL,数据完全够用。

四种采样频率对比

  • SENSOR_DELAY_FASTEST —— 最快,约 0ms 延迟。适合游戏、实时绘图
  • SENSOR_DELAY_GAME —— 较快,约 20ms。适合需要快速响应的场景
  • SENSOR_DELAY_UI —— 适中,约 60ms。适合更新 UI
  • SENSOR_DELAY_NORMAL —— 默认,约 200ms。适合大多数场景

我的建议:除非你真的需要毫秒级响应,否则别用 FASTEST。电池扛不住,用户会骂的。

3.2 onSensorChanged 回调——数据来了

注册完监听,系统就会在传感器数据变化时调用 onSensorChanged。这个方法是整个传感器编程的核心。

@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 或做逻辑处理
    }
}

SensorEvent 里最重要的就是 values 数组。不同传感器,数组长度和含义不一样:

  • 加速度计:values[0] = X轴,values[1] = Y轴,values[2] = Z轴,单位 m/s²
  • 陀螺仪:values[0~2] = 绕 X/Y/Z 轴的角速度,单位 rad/s
  • 光线传感器:values[0] = 光照强度,单位 lux

嗯,这里要注意:onSensorChanged 是在 子线程 调用的。你不能在里面直接更新 UI,否则会崩溃。我刚开始做的时候就被坑过——直接往 TextView 里 setText,结果闪退。正确做法是用 Handler 或者 runOnUiThread。

重要提醒:onSensorChanged 的调用频率取决于你注册时设置的采样周期。如果你用 FASTEST,这个方法可能每秒被调用几百次。不要在回调里做耗时操作,比如写文件、网络请求。否则 UI 线程会被卡死。

3.3 处理传感器精度变化

还有一个回调方法,很多人会忽略:onAccuracyChanged

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
    switch (accuracy) {
        case SensorManager.SENSOR_STATUS_UNRELIABLE:
            // 数据不可靠,建议提示用户
            break;
        case SensorManager.SENSOR_STATUS_ACCURACY_LOW:
            // 精度低,但还能用
            break;
        case SensorManager.SENSOR_STATUS_MEDIUM:
            // 中等精度
            break;
        case SensorManager.SENSOR_STATUS_HIGH:
            // 高精度,放心用
            break;
    }
}

这个回调什么时候触发?比如你拿着手机从室内走到室外,光线传感器精度会变化。或者陀螺仪需要校准的时候。我曾经在做一个 AR 导航项目时,发现陀螺仪数据突然飘了,就是因为精度从 HIGH 降到了 LOW。当时没处理这个回调,结果导航箭头乱转,用户差点投诉。

经验之谈:精度变化回调里,至少要对 SENSOR_STATUS_UNRELIABLE 做处理。可以弹个 Toast 提示用户「传感器异常,请重启应用」。别让用户莫名其妙。

3.4 注销监听——别忘了收尾

注册了监听,就一定要注销。否则 Activity 销毁了,传感器还在跑,电池继续耗。这是新手最容易犯的错误。

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

为什么在 onPause 注销而不是 onDestroy?因为用户按 Home 键回到桌面时,onPause 会执行,但 onDestroy 不一定。如果你在 onDestroy 才注销,用户切到桌面后传感器还在工作,白白耗电。

血的教训:我曾经有个版本忘记在 onPause 注销监听,结果用户反馈手机待机一晚上掉电 30%。查了半天,发现是加速度计一直在跑。从那以后,我每次写传感器代码,第一件事就是把注册和注销的成对代码写好,再写业务逻辑。

3.5 完整代码骨架

把上面这些串起来,就是一个完整的传感器应用骨架:

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(Context.SENSOR_SERVICE);
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    }

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

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

    @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];
            // 在这里处理数据
        }
    }

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

这个骨架,你直接复制就能跑。但别急着复制,先理解每一行在干什么。

3.6 本章知识体系

我把整个流程画了张图,方便你理解:

传感器监听生命周期 1. 获取 SensorManager 2. 获取 Sensor 对象 3. registerListener() 4. onSensorChanged() + onAccuracyChanged() 5. unregisterListener()

这张图把整个流程串起来了。你写代码的时候,就按这个顺序来:获取管理器 → 获取传感器 → 注册 → 处理回调 → 注销。少一步都不行。

好了,第一个传感器应用的核心内容就这些。代码不多,但每一步都有讲究。你动手写一遍,跑起来,再试试不同采样频率的效果,感受一下数据的变化。实践出真知。


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