19、触发传感器事件:使用 triggerSensor 实现单次触发

各位同学,今天我们来聊一个很有意思的话题——单次触发

前面我们一直在用 registerListener 监听传感器,那是持续不断的。但有些场景下,你只需要传感器响应一次。比如:

  • 摇一摇手机触发某个动作
  • 翻转手机静音
  • 拍一下手机亮屏

这种「一次就好」的需求,用 triggerSensor 再合适不过了。

什么是 triggerSensor?

triggerSensor 是 Android 提供的一个一次性传感器触发机制。你调用它,传感器就工作一次,返回数据后自动停止。说白了,就是「用完即走」,不占资源。

我个人习惯把它比作「按门铃」——你按一下,门铃响一声,完事。而 registerListener 就像一直开着门,谁路过都响一下。

哪些传感器支持单次触发?

不是所有传感器都支持 triggerSensor。目前主要支持的是:

传感器类型 说明
TYPE_SIGNIFICANT_MOTION 检测到显著移动时触发一次
TYPE_STEP_DETECTOR 每走一步触发一次
TYPE_TILT_DETECTOR 检测到倾斜时触发一次

嗯,这里要注意:陀螺仪本身不支持单次触发。但我们可以用陀螺仪数据配合 triggerSensor 的思路,自己实现类似效果。这个后面会讲。

核心 API:TriggerEventListener

使用 triggerSensor 需要两个关键对象:

  • SensorManager —— 传感器管理器
  • TriggerEventListener —— 一次性监听器

代码结构其实很简单:

// 1. 获取传感器管理器
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

// 2. 获取传感器实例
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

// 3. 创建一次性监听器
TriggerEventListener listener = new TriggerEventListener() {
    @Override
    public void onTrigger(TriggerEvent event) {
        // 这里只执行一次
        float timestamp = event.timestamp;
        boolean triggered = event.values[0] == 1.0f;
        
        if (triggered) {
            Log.d("SensorDemo", "检测到显著移动!时间戳:" + timestamp);
        }
    }
};

// 4. 注册单次触发
sensorManager.requestTriggerSensor(listener, sensor);

看到没?没有 onAccuracyChanged,没有 onSensorChanged 的持续回调。就一个 onTrigger,触发完就结束。

关键区别:

  • registerListener → 持续监听,需要手动 unregister
  • requestTriggerSensor → 触发一次,自动注销

实战:用陀螺仪实现「摇一摇单次触发」

虽然陀螺仪没有原生单次触发,但我们可以自己封装。我在项目中就遇到过这种需求——用户摇一下手机切换歌曲,但不能一直摇,摇一次就停。

思路是这样的:

  1. registerListener 监听陀螺仪
  2. 检测到角速度超过阈值时,执行一次动作
  3. 立即注销监听器,实现「单次」效果

代码实现:

public class ShakeOnceDetector {
    private SensorManager sensorManager;
    private Sensor gyroscope;
    private SensorEventListener listener;
    private static final float SHAKE_THRESHOLD = 3.0f; // 弧度/秒
    
    public void startSingleShakeDetection() {
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        
        listener = new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent event) {
                float x = event.values[0];
                float y = event.values[1];
                float z = event.values[2];
                
                double magnitude = Math.sqrt(x*x + y*y + z*z);
                
                if (magnitude > SHAKE_THRESHOLD) {
                    // 检测到摇动,执行动作
                    onShakeDetected();
                    
                    // 关键:立即注销,实现单次触发
                    sensorManager.unregisterListener(this);
                }
            }
            
            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
                // 不需要处理
            }
        };
        
        sensorManager.registerListener(listener, gyroscope, 
                                       SensorManager.SENSOR_DELAY_FASTEST);
    }
    
    private void onShakeDetected() {
        Log.d("ShakeOnce", "摇动检测完成!已自动停止监听。");
        // 这里放你的业务逻辑
    }
}

小技巧:如果你想让用户再次触发,可以提供一个 reset() 方法,重新调用 registerListener。这样既实现了「单次」,又保留了「可重复」的能力。

避坑指南

我曾经踩过一个坑:用 triggerSensor 监听 TYPE_SIGNIFICANT_MOTION,结果在模拟器上怎么都触发不了。后来才发现,这个传感器需要真实的硬件支持,模拟器基本不模拟。

还有一点:单次触发不等于低功耗。虽然它只触发一次,但触发前的监听过程仍然会消耗电量。别指望用它来省电,省的是你的代码复杂度。

注意事项:

  • triggerSensor 不支持所有传感器,使用前先检查 sensor.isTriggerSensor()
  • 触发后监听器自动注销,不需要手动 unregister
  • 如果传感器不支持,requestTriggerSensor 会返回 false
  • 在 Android 4.4+ 才支持 TriggerEventListener

核心逻辑流程图

下面这张图帮你理清 triggerSensor 和自实现单次触发的区别:

单次触发 vs 持续监听 流程对比 requestTriggerSensor() 传感器工作 → 返回数据 onTrigger() 回调一次 自动注销 · 不再监听 registerListener() onSensorChanged() 持续回调 判断阈值 → 执行动作 手动 unregisterListener() 原生单次触发 自实现单次触发

总结一下

triggerSensor 是个好东西,但适用范围有限。对于陀螺仪这种不支持原生单次触发的传感器,我们完全可以自己动手封装。你想想看,核心就三步:监听 → 判断 → 注销。

我个人建议:如果你的传感器支持原生单次触发,优先用 triggerSensor。如果不支持,就用我上面那个「摇一摇」的模板,改改阈值就能用。

嗯,今天就到这里。代码不多,但思路很重要。下次你遇到「只触发一次」的需求,应该知道怎么做了吧?


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