2、Android传感器框架:SensorManager、Sensor、SensorEvent 三大核心类介绍

好,咱们正式开始接触Android传感器的核心代码层。

上一章我们聊了陀螺仪能干什么,这一章,我们来拆解一下Android到底是怎么让你拿到传感器数据的。说白了,就是三个类:SensorManagerSensorSensorEvent

我刚开始接触Android传感器时,也被这三个类绕得有点晕。后来发现,其实它们的关系特别简单——一个管注册,一个管描述,一个管数据。你只要记住这个口诀,后面就顺了。

核心口诀:

  • SensorManager —— 传感器管理员,负责获取传感器列表、注册/注销监听器。
  • Sensor —— 传感器本身的信息卡,告诉你这个传感器叫什么、什么类型、精度如何。
  • SensorEvent —— 传感器的一次数据快照,包含时间戳、数值数组、精度等。
Android 传感器三大核心类关系图 SensorManager 获取系统服务 注册/注销监听器 Sensor 传感器元信息 类型、名称、分辨率 SensorEvent 传感器数据事件 values[]、timestamp getDefaultSensor() onSensorChanged() 工作流程: 1. 通过 SensorManager 获取 Sensor 对象(如陀螺仪) 2. 使用 SensorManager 注册 SensorEventListener 3. 系统回调 onSensorChanged(),传递 SensorEvent 对象 4. 从 SensorEvent.values[] 中读取 x、y、z 轴数据

2.1 SensorManager —— 传感器的“大管家”

SensorManager 是你和传感器硬件之间的桥梁。你想想看,手机里那么多传感器——陀螺仪、加速度计、磁力计、光线传感器……总得有个统一的管理入口吧?SensorManager 就是干这个的。

获取它的方式很简单:

SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

嗯,这里要注意:SensorManager 是一个系统服务,你不需要自己去 new 它。Android 系统在开机时就已经初始化好了。

我个人习惯在 Activity 的 onCreate() 里获取 SensorManager,然后在 onResume() 里注册监听器,onPause() 里注销。为什么?因为传感器很耗电,你不看它的时候就应该关掉。

避坑指南:

我曾经在项目中犯过一个低级错误——在 onCreate() 里注册了监听器,但忘了在 onPause() 里注销。结果用户切到后台后,陀螺仪还在疯狂上报数据,电量哗哗地掉。后来被测试小姐姐追着骂了一周……

所以记住:注册和注销一定要成对出现

2.2 Sensor —— 传感器的“身份证”

Sensor 对象本身不包含实时数据,它只描述传感器本身的属性。你可以把它理解成传感器的“身份证”。

常用的方法有这些:

方法 返回值 说明
getName() String 传感器名称,比如 "MPU-6500"
getType() int 传感器类型常量,陀螺仪是 Sensor.TYPE_GYROSCOPE
getResolution() float 传感器分辨率,单位取决于传感器类型
getMaximumRange() float 传感器最大量程
getPower() float 传感器功耗,单位 mA
getVendor() String 传感器供应商,比如 "InvenSense"

获取陀螺仪 Sensor 对象的方式:

Sensor gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
if (gyroscope == null) {
    // 你的手机没有陀螺仪!这种情况在低端机上偶尔会遇到
}

为什么用 getDefaultSensor() 而不是 getSensorList()?因为大多数情况下,系统会给你返回一个“最合适”的传感器。如果手机上有多个同类型传感器(比如一个高精度但费电,一个低精度但省电),系统会帮你选一个默认的。

注意:

不是所有手机都有陀螺仪!我测试过一款百元机,getDefaultSensor(Sensor.TYPE_GYROSCOPE) 返回了 null。所以一定要做空值判断,否则你的 App 会在那些手机上直接崩溃。

2.3 SensorEvent —— 传感器的“数据快照”

终于到了最核心的部分——拿到数据。

当传感器有新的数据时,系统会回调 onSensorChanged(SensorEvent event) 方法。这个 event 对象里包含了所有你需要的信息。

SensorEvent 的主要成员:

成员 类型 说明
values float[] 传感器数据数组,陀螺仪是 3 个值:x、y、z 轴角速度
sensor Sensor 触发该事件的 Sensor 对象
accuracy int 精度等级:0=不可靠,1=低,2=中,3=高
timestamp long 事件发生的时间戳,单位纳秒

陀螺仪的 values[] 数组含义:

  • values[0] —— 绕 X 轴的角速度(单位:rad/s),也就是手机的横滚(Roll)
  • values[1] —— 绕 Y 轴的角速度(单位:rad/s),也就是手机的俯仰(Pitch)
  • values[2] —— 绕 Z 轴的角速度(单位:rad/s),也就是手机的偏航(Yaw)

一个完整的监听器实现:

private SensorEventListener gyroListener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];
            
            // 这里就可以用 x、y、z 做你想做的事了
            // 比如计算旋转角度、检测手机是否被拿起等等
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 精度变化时回调,一般不需要处理
        // 但如果 accuracy 突然变成 0,说明传感器出问题了
    }
};

关键点:

陀螺仪返回的是角速度,不是角度。如果你想得到手机旋转了多少度,需要对角速度做积分。这个我们后面会详细讲,现在先记住:values[] 的单位是 弧度/秒 (rad/s)

2.4 注册与注销 —— 生命周期管理

有了上面三个类,最后一步就是把它们串起来。注册监听器的标准写法:

@Override
protected void onResume() {
    super.onResume();
    sensorManager.registerListener(
        gyroListener,          // 监听器
        gyroscope,             // 传感器对象
        SensorManager.SENSOR_DELAY_NORMAL  // 采样频率
    );
}

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

采样频率有四个可选值:

常量 说明 典型延迟
SENSOR_DELAY_NORMAL 普通游戏或屏幕旋转 约 200ms
SENSOR_DELAY_UI UI 更新场景 约 60ms
SENSOR_DELAY_GAME 游戏场景 约 20ms
SENSOR_DELAY_FASTEST 最快频率,不限制 取决于硬件

我的建议:

不要一上来就用 FASTEST。我曾经为了追求“实时性”把采样率设成最快,结果手机烫得能煎鸡蛋。后来发现,对于大多数场景,GAME 甚至 NORMAL 就完全够用了。省电又凉快,何乐而不为?

2.5 小结

好了,这一章的核心内容就这些。总结一下:

  • SensorManager 负责获取传感器和注册监听器
  • Sensor 描述传感器的元信息,不包含实时数据
  • SensorEvent 是数据载体,values[] 里存着陀螺仪的 x、y、z 角速度
  • 记得在 onPause() 里注销监听器,别让电量偷偷溜走

下一章我们会真正动手写代码,让陀螺仪的数据在屏幕上动起来。不过在那之前,建议你把这三个类的 API 文档翻一翻,尤其是 SensorEvent 的 timestamp 字段——它在后面做数据融合时会非常有用。


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