14、多传感器协同:光线传感器与距离传感器配合,实现防误触(通话时灭屏)

各位同学,今天我们来聊一个非常实用的场景——通话时屏幕自动熄灭,防止你的脸颊误触挂断键。

这个功能,说白了就是光线传感器和距离传感器一起干活。我最早接触这个需求,是在做一款低端功能机的时候。当时用户反馈说“打电话时脸把录音打开了”,嗯,从那以后我就特别重视这个防误触逻辑。

14.1 为什么需要两个传感器?

你可能会问:一个距离传感器不就够了吗?

理论上是的。但实际项目中,距离传感器有个“坑”——它只能告诉你“有没有东西靠近”,但判断不了“是不是在口袋里”。

举个例子:

  • 你把手机放桌上,距离传感器检测到“远”,屏幕亮着——没问题。
  • 你把手机贴到耳边,距离传感器检测到“近”,屏幕熄灭——也没问题。
  • 但如果你把手机塞进裤兜,距离传感器也会检测到“近”,屏幕熄灭——这时候如果来电,你根本看不到是谁打的。

所以,我们需要光线传感器来帮忙。光线传感器能告诉你“当前环境光有多亮”。

我个人的习惯是:

  • 通话时,如果距离传感器检测到“近”光线传感器检测到“暗”,才判定为“贴在耳边”,执行灭屏。
  • 如果距离传感器检测到“近”但光线传感器检测到“亮”,说明可能只是手指遮挡,不灭屏。

这样就能避免裤兜里误灭屏的问题。

14.2 核心逻辑流程图

下面这张图,是我自己项目里一直用的逻辑。你照着写,基本不会出大问题。

通话开始 距离传感器:是否检测到物体靠近? (阈值通常设为 5cm) 保持亮屏 光线传感器:环境光是否低于阈值? (通常设为 10 lux 以下) 保持亮屏 执行灭屏 通话结束或传感器状态变化时重新判断

14.3 代码实现:SensorManager 协同监听

好,逻辑清楚了,我们来看代码。Android 里监听多个传感器,其实就是在 SensorEventListener 里同时处理多个传感器类型。

我习惯的做法是:

  • 注册两个传感器:TYPE_PROXIMITYTYPE_LIGHT
  • onSensorChanged 里分别处理
  • 用一个状态机来管理当前是否应该灭屏
public class CallScreenManager implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor proximitySensor;
    private Sensor lightSensor;
    
    private boolean isNear = false;      // 距离传感器状态
    private float currentLux = 1000f;    // 光线传感器值,默认亮
    
    private static final float PROXIMITY_THRESHOLD_CM = 5.0f;
    private static final float LIGHT_THRESHOLD_LUX = 10.0f;
    
    public CallScreenManager(Context context) {
        sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
    }
    
    public void startListening() {
        sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
        sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
    
    public void stopListening() {
        sensorManager.unregisterListener(this);
    }
    
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
            // 距离传感器:大多数返回 cm,但有些返回 0/1
            float distance = event.values[0];
            isNear = distance < PROXIMITY_THRESHOLD_CM;
            // 注意:有些传感器最大值就是阈值,比如 max=5cm
            if (distance >= event.sensor.getMaximumRange()) {
                isNear = false;  // 超出范围,视为远
            }
        } else if (event.sensor.getType() == Sensor.TYPE_LIGHT) {
            currentLux = event.values[0];
        }
        
        // 协同判断
        updateScreenState();
    }
    
    private void updateScreenState() {
        boolean shouldScreenOff = isNear && (currentLux < LIGHT_THRESHOLD_LUX);
        // 这里调用系统接口灭屏或亮屏
        // 比如通过 PowerManager 或 WindowManager
        if (shouldScreenOff) {
            // 灭屏
            Log.d("CallScreen", "灭屏:靠近且环境光暗");
        } else {
            // 亮屏
            Log.d("CallScreen", "亮屏:远离或环境光亮");
        }
    }
    
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 一般不用处理
    }
}

关键点:

  • 距离传感器的 maximumRange 很重要。有些手机最大返回 5cm,有些是 10cm。你写死阈值容易出问题。
  • 光线传感器的 lux 值在口袋里通常是 0~5,在室内是 100~500,在户外是 10000+。10 lux 是个比较安全的阈值。

14.4 避坑指南:我踩过的三个坑

坑一:距离传感器返回值不统一

我曾经遇到过一款手机,距离传感器只返回 0 和 1。0 表示近,1 表示远。但另一款手机返回的是实际厘米数。所以代码里一定要判断 maximumRange,不能写死。

坑二:光线传感器在通话时被遮挡

有些手机的距离传感器和光线传感器是分开的,但位置很近。如果你用手遮挡距离传感器,光线传感器也可能被挡住,导致 lux 值骤降。这时候就会误判为“暗”。

我的解决办法是:加一个延时。比如连续 3 次检测到“近且暗”才执行灭屏,避免瞬间遮挡误触发。

坑三:亮屏时不要频繁调用 PowerManager

有些同学在 onSensorChanged 里直接调用 PowerManager.goToSleep()wakeLock.acquire()。但传感器回调频率很高(尤其是光线传感器),频繁操作电源管理会导致系统卡顿甚至 ANR。

我建议:用一个标志位记录当前屏幕状态,只有状态变化时才去操作电源。

14.5 性能优化建议

优化点 做法 原因
传感器采样率 使用 SENSOR_DELAY_NORMAL 而非 FASTEST 通话场景不需要高频采样,200ms 一次足够
状态变化检测 只在状态变化时更新 UI 或电源 避免频繁调用系统接口
取消注册 通话结束后及时 unregisterListener 防止后台持续耗电
阈值滞后 灭屏阈值和亮屏阈值留一点差值 防止在临界值附近频繁切换

小技巧:阈值滞后是什么意思?比如灭屏条件是 lux < 10,亮屏条件是 lux > 15。中间 10~15 这个区间保持原状态不变。这样就不会因为光线轻微波动而反复亮灭屏。

注意:有些手机在通话时,系统会自动接管传感器。比如 MIUI、EMUI 等定制系统,可能会拦截你的传感器注册。如果你发现自己的代码在某个手机上不生效,先检查一下系统是否已经帮你做了防误触。

好了,关于光线传感器和距离传感器的协同,今天就聊到这里。这个功能虽然看起来简单,但实际项目中要考虑的细节不少。尤其是不同厂商的传感器行为差异,真的让人头大。不过只要你掌握了核心逻辑——距离判断 + 光线判断 + 状态机管理,基本就能覆盖 90% 的场景。

下次你打电话时,可以留意一下屏幕熄灭的时机,想想背后两个传感器是怎么配合的。嗯,这就是工程师的浪漫吧。


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