9、距离传感器实战:接近检测原理、防误触实现、距离传感器在智能家居中的应用、功耗优化

距离传感器,说白了就是手机顶部那个不起眼的小黑点。你打电话时屏幕自动熄灭,靠的就是它。我最早接触这个传感器是在做一款老人机项目时,那时候才发现,一个小小的距离传感器,坑还真不少。

9.1 接近检测原理

Android 里的距离传感器,大部分是红外方案。发射管发出一束红外光,碰到物体反射回来,接收管收到信号。芯片根据发射和接收的时间差,或者光强衰减,算出距离。

嗯,这里要注意:大多数手机的距离传感器只有「远」和「近」两个状态,不是连续测距。你拿尺子量是量不出精确厘米数的。

核心 API 调用

SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);

sensorManager.registerListener(new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        float distance = event.values[0];
        // 大部分手机:distance = 0.0 表示贴近,distance = 5.0 表示远离
        if (distance < event.sensor.getMaximumRange()) {
            // 接近状态
        } else {
            // 远离状态
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 精度变化,一般不用处理
    }
}, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);

我个人习惯把 getMaximumRange() 作为阈值。为什么?因为不同厂商的传感器最大范围不一样,有的是 5cm,有的是 8cm。写死 5.0 的话,换台手机就翻车了。

9.2 防误触实现

防误触,这是距离传感器最经典的应用场景。我在项目中遇到过一个问题:用户把手机放口袋里,屏幕被误点亮,结果在口袋里拨出了紧急电话。

解决方案其实不复杂:

  1. 监听距离传感器状态:当 distance 接近 0 时,标记为「口袋模式」
  2. 屏蔽触摸事件:在口袋模式下,拦截所有触摸事件
  3. 退出条件:只有当距离传感器返回「远」状态,且持续 1 秒以上,才退出口袋模式

避坑指南:我曾经在退出条件上只判断了一次「远」状态,结果用户从口袋拿出手机时,手还没离开传感器,屏幕就亮了,又误触了。后来加了个 1 秒的延时,问题解决。

private boolean isInPocket = false;
private long lastFarTime = 0;

@Override
public void onSensorChanged(SensorEvent event) {
    float distance = event.values[0];
    float maxRange = event.sensor.getMaximumRange();

    if (distance < maxRange) {
        // 接近 -> 进入口袋模式
        isInPocket = true;
        lastFarTime = 0;
    } else {
        // 远离 -> 延迟退出
        if (isInPocket) {
            if (lastFarTime == 0) {
                lastFarTime = System.currentTimeMillis();
            } else if (System.currentTimeMillis() - lastFarTime > 1000) {
                isInPocket = false;
            }
        }
    }
}

9.3 距离传感器在智能家居中的应用

你以为距离传感器只用在手机上?其实智能家居里用得更多。我参与过一个智能灯控项目,用距离传感器检测人是否在附近,实现人来灯亮、人走灯灭。

典型的应用场景:

场景 实现方式 注意事项
智能灯控 检测到人接近 -> 开灯;人离开 -> 延时关灯 延时时间要可配置,避免频繁开关
智能门锁 手靠近门把手 -> 唤醒指纹模块 功耗要低,否则电池扛不住
智能水龙头 手伸到感应区 -> 出水;手离开 -> 停水 响应速度要快,延迟超过 200ms 体验就很差
智能垃圾桶 人靠近 -> 桶盖自动打开 要区分人和宠物,不然猫路过盖子就开了

你想想看,这些场景本质上都是在做同一件事:用距离传感器判断「有没有人/手在附近」。只是阈值、延时、功耗策略不同而已。

9.4 功耗优化

功耗优化,这是距离传感器开发里最容易被忽视的一环。我见过不少 App,一启动就注册传感器监听,退到后台也不注销,结果电量哗哗地掉。

几个实用的优化策略:

  • 按需注册:只在需要的时候注册监听,不需要就注销。比如打电话时注册,挂断后注销。
  • 降低采样率:防误触场景不需要高频率,用 SENSOR_DELAY_NORMAL 就够了。用 SENSOR_DELAY_FASTEST 纯属浪费。
  • 后台限制:App 进入后台后,如果不需要距离传感器,立即注销。Android 8.0 以后,后台 App 的传感器功耗会被系统限制,但最好还是主动管理。
  • 批量处理:如果多个功能都需要距离传感器,合并成一个监听器,不要重复注册。

警告:不要在 onResume() 里注册、onPause() 里注销。因为 Activity 频繁切换时,传感器会反复开关,反而更耗电。建议在 onStart()onStop() 里操作。

@Override
protected void onStart() {
    super.onStart();
    // 注册传感器
    sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onStop() {
    super.onStop();
    // 注销传感器
    sensorManager.unregisterListener(this);
}

还有一个细节:距离传感器在大部分手机上,即使不注册监听,系统也会在通话时自动启用。所以如果你只是做防误触,可以依赖系统行为,不用自己再写一套。但如果你需要自定义逻辑(比如游戏防误触),那就得自己来了。

9.5 知识体系总览

下面这张图,把本章的核心逻辑串起来了。从传感器原理到应用场景,再到功耗优化,一条线走到底。

距离传感器实战知识体系 接近检测原理 防误触实现 智能家居应用 功耗优化 口袋模式检测 触摸事件拦截 延时退出机制 智能灯控 / 门锁 智能水龙头 / 垃圾桶 人来灯亮 / 人走灯灭 按需注册 / 注销 降低采样率 后台限制 / 批量处理

距离传感器看起来简单,但要做好,细节不少。从原理到防误触,从智能家居到功耗优化,每一步都有坑。我踩过的坑,希望你别再踩一遍。

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