5、获取Sensor对象:使用getDefaultSensor获取距离传感器

好,咱们继续。上一节我们把SensorManager拿到手了,现在要干正事了——获取距离传感器对象。

说白了,SensorManager就像个传感器仓库管理员。你告诉他“我要距离传感器”,他就给你对应的Sensor对象。如果仓库里没有这个货,他就给你个null。

5.1 标准获取方式

代码其实就一行,但我建议你把它写成一个完整的获取逻辑。我在项目中见过太多人直接拿返回值就用,结果设备不支持时直接崩了。

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

嗯,这里要注意:getDefaultSensor()返回的是系统推荐的默认距离传感器。大多数手机只有一个距离传感器,所以这个方法够用了。

5.2 处理传感器为null的情况

你想想看,如果设备不支持距离传感器,getDefaultSensor()会返回null。这时候你要是直接调用proximitySensor.getName(),妥妥的NullPointerException。

我曾经在一个平板上遇到过这个问题。那台平板没有距离传感器,但代码里没做判空,结果上线后崩溃率直接飙了。从那以后,我养成了一个习惯:

核心原则:获取传感器后,必须判空。

if (proximitySensor == null) {
    // 设备不支持距离传感器
    Log.w(TAG, "设备不支持距离传感器");
    // 可以提示用户,或者禁用相关功能
    return;
}

// 正常使用
Log.i(TAG, "距离传感器: " + proximitySensor.getName());

5.3 获取所有距离传感器

有时候设备可能有多个距离传感器(比如前置和后置)。getDefaultSensor()只返回默认的那个。如果你想拿到所有距离传感器,可以用:

List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
if (sensorList.isEmpty()) {
    Log.w(TAG, "没有找到任何距离传感器");
} else {
    Log.i(TAG, "找到 " + sensorList.size() + " 个距离传感器");
    for (Sensor sensor : sensorList) {
        Log.i(TAG, "传感器名称: " + sensor.getName() + 
              ", 供应商: " + sensor.getVendor());
    }
}

我个人习惯用getDefaultSensor(),因为大多数场景下只需要一个。但如果你在做多传感器融合或者特殊硬件适配,getSensorList()会更灵活。

5.4 传感器信息一览

拿到Sensor对象后,你可以获取它的详细信息。这些信息在调试和适配时特别有用:

方法 返回值 说明
getName() String 传感器名称,比如"STK3328 Proximity"
getVendor() String 供应商,比如"STMicroelectronics"
getVersion() int 传感器版本号
getMaximumRange() float 最大检测距离,单位cm。典型值5.0或8.0
getResolution() float 分辨率,即最小变化值
getPower() float 功耗,单位mA。距离传感器通常很低,0.5-2mA
getMinDelay() int 最小采样间隔,单位微秒

小技巧:调试时可以把这些信息打印出来,看看不同手机的距离传感器有什么差异。我遇到过一款手机,它的距离传感器最大范围是8.0cm,但实际有效距离只有3cm。这种坑,不打印出来根本发现不了。

5.5 完整代码示例

下面是一个完整的获取距离传感器的工具方法,我直接贴出来:

public class SensorHelper {
    private static final String TAG = "SensorHelper";
    
    private SensorManager mSensorManager;
    private Sensor mProximitySensor;
    
    public SensorHelper(Context context) {
        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        initProximitySensor();
    }
    
    private void initProximitySensor() {
        if (mSensorManager == null) {
            Log.e(TAG, "获取SensorManager失败");
            return;
        }
        
        mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        
        if (mProximitySensor == null) {
            Log.w(TAG, "设备不支持距离传感器");
            // 这里可以回调给UI层,提示用户
            return;
        }
        
        // 打印传感器信息,方便调试
        Log.i(TAG, "距离传感器信息:");
        Log.i(TAG, "  名称: " + mProximitySensor.getName());
        Log.i(TAG, "  供应商: " + mProximitySensor.getVendor());
        Log.i(TAG, "  最大范围: " + mProximitySensor.getMaximumRange() + " cm");
        Log.i(TAG, "  分辨率: " + mProximitySensor.getResolution());
        Log.i(TAG, "  功耗: " + mProximitySensor.getPower() + " mA");
    }
    
    public Sensor getProximitySensor() {
        return mProximitySensor;
    }
    
    public boolean isProximitySensorAvailable() {
        return mProximitySensor != null;
    }
}

注意:不要在主线程里做耗时的传感器初始化操作。虽然获取Sensor对象本身很快,但后续注册监听器、处理事件都是在主线程回调的。如果处理不当,容易卡UI。

5.6 知识体系图

我把这一章的核心逻辑画成了流程图,方便你理解:

获取距离传感器核心流程 获取SensorManager getDefaultSensor(TYPE_PROXIMITY) 是否为null? 设备不支持 返回null 获取成功 得到Sensor对象 核心原则:获取后必须判空,避免NullPointerException

5.7 常见问题与避坑

  • 问题:有些手机的距离传感器在系统设置里可以关闭,这时候getDefaultSensor()返回什么?
    答案:返回的还是Sensor对象,但注册监听器后可能收不到事件。我建议在注册监听器后,先主动获取一次数据,确认传感器是否正常工作。
  • 问题:模拟器上能获取到距离传感器吗?
    答案:大部分模拟器不支持距离传感器,会返回null。所以测试时尽量用真机。
  • 问题:获取传感器需要权限吗?
    答案:不需要。获取Sensor对象、注册监听器都不需要额外权限。但如果你要读取其他敏感数据(比如位置),那就另说了。

我的经验:在应用启动时,先检查距离传感器是否可用。如果不可用,直接禁用相关功能,而不是在运行时崩溃。用户体验会好很多。

好了,这一章就到这里。记住:拿到Sensor对象只是第一步,后面还有注册监听器、处理事件、注销监听器等一系列操作。但基础打牢了,后面就顺了。


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