20、动态传感器管理:在 onResume 注册、onPause 注销监听器

说实话,这个话题我特别想跟你好好聊聊。

为什么?因为我在早期做传感器开发时,就因为这个看似简单的「注册/注销」问题,踩过一个大坑。那会儿刚接手一个运动追踪App,用户反馈说手机发烫、电量掉得飞快。我排查了半天,最后发现——传感器监听器在Activity退到后台后,居然还在疯狂上报数据。

嗯,从那以后,我再也不敢小看「动态管理」这四个字了。

为什么非要动态管理?

你想想看,传感器一旦注册了监听器,它就会持续不断地往CPU扔数据。哪怕你的界面已经退到后台,哪怕用户已经把手机揣兜里了,它还在工作。

这会带来三个直接后果:

  • 电量浪费——传感器芯片和CPU都在空转
  • CPU负载升高——回调函数被频繁调用,可能引发卡顿
  • 数据污染——后台采集的数据对前台界面毫无意义

说白了,这就是一种资源泄漏。跟忘记关闭Cursor、忘记释放Bitmap是一个道理。

核心原则:传感器监听器的生命周期,必须与Activity/Fragment的可见生命周期绑定。

可见时注册,不可见时注销。就这么简单。

标准做法:onResume / onPause 配对

我个人习惯把传感器注册放在 onResume(),注销放在 onPause()。为什么不是 onStart() / onStop()

因为 onResume()onPause() 才真正对应「界面可见且可交互」的状态。举个例子,如果系统弹出一个透明对话框,Activity会走 onPause() 但不会走 onStop()。这时候传感器就该停掉,因为用户已经看不到你的界面了。

public class SensorActivity extends AppCompatActivity {

    private SensorManager sensorManager;
    private Sensor accelerometer;
    private SensorEventListener listener;

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

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

        listener = new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent event) {
                // 处理传感器数据
                float x = event.values[0];
                float y = event.values[1];
                float z = event.values[2];
                // 更新UI...
            }

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

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

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

小提示:onPause() 中注销时,不需要判空。因为 unregisterListener() 方法内部已经做了空安全处理。传一个 null 进去也不会崩溃。

避坑指南:我曾经犯过的错

我曾经在 onDestroy() 里注销监听器,觉得「反正Activity要销毁了,这时候注销最保险」。结果呢?用户快速旋转手机时,Activity重建走了 onPause() → onDestroy() → onCreate() → onResume()。如果 onPause() 里没注销,而 onDestroy() 里才注销,中间那段时间传感器一直在跑。更糟的是,如果 onDestroy() 没被调用(比如系统杀进程时),监听器就永远泄漏了。

还有一次,我忘了在 onPause() 里注销,结果用户把App切到后台,传感器还在疯狂上报。那款手机的陀螺仪功耗本来就高,半小时后手机直接过热保护了。嗯,用户给了个一星差评,说「这App是暖手宝吗?」

警告:千万不要在 onSaveInstanceState() 中注销传感器。这个方法不保证在Activity销毁前被调用,而且它的设计目的是保存UI状态,不是释放资源。

多传感器场景下的管理策略

如果你的页面同时使用了多个传感器(比如加速度计+陀螺仪+磁力计),我建议统一管理,而不是分散注册。

private List<Sensor> sensorList = new ArrayList<>();

private void registerAllSensors() {
    sensorList.clear();
    addSensor(Sensor.TYPE_ACCELEROMETER);
    addSensor(Sensor.TYPE_GYROSCOPE);
    addSensor(Sensor.TYPE_MAGNETIC_FIELD);

    for (Sensor sensor : sensorList) {
        sensorManager.registerListener(listener, sensor,
                SensorManager.SENSOR_DELAY_GAME);
    }
}

private void addSensor(int type) {
    Sensor s = sensorManager.getDefaultSensor(type);
    if (s != null) {
        sensorList.add(s);
    }
}

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

这样做的好处是:注销时只需要一行代码,所有传感器一次性解绑。而且 unregisterListener() 会遍历内部列表,把该监听器绑定的所有传感器都注销掉,不用担心遗漏。

关于采样频率的补充

注册传感器时,第三个参数是采样延迟。我见过有人为了省电,在 onResume() 里用 SENSOR_DELAY_UI,在 onPause() 里注销。这没问题。

但如果你需要在后台保持低频率采集(比如计步器场景),那就不能简单地在 onPause() 里注销了。这时候应该考虑 前台服务(Foreground Service),把传感器管理放到Service里,用通知告诉用户「我正在后台使用传感器」。这是另一个话题,我们后面会讲到。

知识体系总览

下面这张图,帮你理清动态传感器管理的核心逻辑:

动态传感器管理核心流程 onCreate() 初始化 SensorManager onResume() 注册监听器 传感器数据采集 注销监听器 onPause() 关键要点 • 配对注册/注销 • 避免在 onDestroy 注销 • 多传感器统一管理 • 注意采样频率选择 • 后台场景用 Service 常见错误: ✗ 忘记注销导致泄漏 ✗ 在错误生命周期注销 ✗ 未处理空指针异常

总结一下

动态传感器管理,说白了就是两件事:

  • 什么时候注册?——界面可见且可交互时,也就是 onResume()
  • 什么时候注销?——界面不可见时,也就是 onPause()

记住这个配对关系,你的App就不会出现传感器泄漏的问题。我见过太多新手在 onCreate() 里注册、在 onDestroy() 里注销,结果各种诡异Bug。其实只要遵循这个简单的生命周期规则,90%的问题都能避免。

嗯,下次如果你遇到手机发烫、电量异常,不妨先检查一下——传感器监听器是不是忘了注销?


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