2. Android传感器框架:Android Sensor Framework 架构概览

好,我们正式开始聊Android的传感器框架。说实话,很多做应用开发的同事,可能一辈子都没碰过HAL层。但如果你要做距离传感器的深度调优,不了解这个框架,就像开车不看仪表盘——能跑,但心里没底。

我个人习惯把Android传感器框架分成三层来看:应用层、框架层、硬件抽象层。这三层各司其职,又紧密配合。今天我们就一层层剥开它。

2.1 应用层:你天天打交道的三个类

应用层说白了就是SDK里暴露给开发者的API。核心就三个类:SensorManagerSensorSensorEventListener。我刚开始做传感器开发时,以为只要new一个SensorManager就能拿到数据了——嗯,后来被坑得不轻。

2.1.1 SensorManager——传感器的大管家

这个类是你获取传感器服务的入口。通过Context.getSystemService(SENSOR_SERVICE)拿到它。它负责三件事:

  • 列举设备上的传感器getSensorList() 返回所有可用传感器列表
  • 获取特定传感器getDefaultSensor() 拿到默认的某个类型传感器
  • 注册/注销监听registerListener()unregisterListener()

重要提醒getDefaultSensor() 返回的不一定是物理传感器。如果设备上有多个同类型传感器,系统会选一个“最优”的。我在项目中遇到过,某款手机的距离传感器有两个,一个在听筒旁边,一个在顶部,系统默认选的那个位置不对,导致通话时屏幕一直亮着。

2.1.2 Sensor——传感器的身份证

Sensor 对象本身不干活,它只是描述传感器的属性。比如:

  • getName():传感器名字,比如 "STK3321 Proximity Sensor"
  • getType():类型,距离传感器是 Sensor.TYPE_PROXIMITY
  • getMaximumRange():最大量程,距离传感器通常是5cm或10cm
  • getResolution():分辨率,决定了数据精度
  • getMinDelay():最小采样间隔,单位微秒

你想想看,如果不知道传感器的量程,你怎么判断返回的“5.0”是5厘米还是5毫米?所以拿到Sensor对象后,第一件事就是读它的属性。

2.1.3 SensorEventListener——数据回调的桥梁

这个接口就两个方法:

public interface SensorEventListener {
    void onSensorChanged(SensorEvent event);
    void onAccuracyChanged(Sensor sensor, int accuracy);
}

onSensorChanged 里你会拿到 SensorEvent,它的 values[] 数组就是传感器数据。距离传感器通常只有一个值,存在 values[0] 里。

我的习惯:在 onSensorChanged 里不要做耗时操作。这个回调频率可能很高(尤其是加速度计),你如果在里面写文件或做网络请求,UI线程直接卡死。我曾经见过一个同事,在回调里写Log写到手软,结果手机烫得能煎鸡蛋。

2.2 框架层:系统服务层的秘密

应用层下面,是系统服务层。这一层在 system_server 进程里运行,叫 SensorService。它负责:

  • 管理所有传感器的注册和注销
  • 控制传感器的采样率和上报频率
  • 把HAL层上报的原始数据转换成应用层能理解的格式
  • 处理权限校验(比如某些传感器需要特定权限)

这里有个关键点:应用层注册监听时指定的采样率,只是一个“建议值”。系统服务层会根据所有注册者的需求,取一个最优的采样率。比如你要求100ms一次,另一个应用要求200ms一次,系统可能就按100ms来采样,然后分别分发给你们。

避坑指南:我曾经在项目里把采样率设成 SENSOR_DELAY_FASTEST(最快模式),结果电池一小时掉光。后来才知道,这个模式下传感器以硬件支持的最高频率上报,距离传感器还好,如果是加速度计,那数据量是惊人的。所以,按需设置采样率,别贪快。

2.3 HAL层:硬件和系统的最后一道关卡

HAL层,全称 Hardware Abstraction Layer。它把硬件厂商的驱动和Android框架隔离开。为什么要有这一层?说白了,就是Google不想管每个厂商的驱动怎么写,只要厂商实现HAL接口,系统就能用。

距离传感器的HAL接口定义在 hardware/libhardware/include/hardware/sensors.h 里。核心结构体是:

struct sensor_t {
    const char*     name;
    const char*     vendor;
    int             version;
    int             handle;
    int             type;
    float           maxRange;
    float           resolution;
    float           power;
    int32_t         minDelay;
    uint16_t        fifoReservedEventCount;
    uint16_t        fifoMaxEventCount;
    const char*     stringType;
    const char*     requiredPermission;
    int32_t         maxDelay;
    uint32_t        flags;
    void*           reserved;
};

你看,这个结构体和应用层的 Sensor 类几乎一一对应。数据就是从HAL层一路传上来的。

2.4 三层关系:一张图说清楚

下面这张图是我自己画的,展示了数据从硬件到应用的完整路径:

Android传感器框架三层架构 应用层 (Application Framework) SensorManager → Sensor → SensorEventListener registerListener() / unregisterListener() / onSensorChanged() Binder IPC 框架层 (System Server) SensorService 采样率管理 / 数据分发 / 权限校验 HAL接口调用 硬件抽象层 (HAL) sensors.h → sensor_t / sensors_poll_device_t 厂商驱动实现 / 原始数据采集 / 中断处理

数据流向是:硬件 → HAL层 → SensorService → SensorManager → 你的应用。反过来,你的注册请求是:应用 → SensorManager → SensorService → HAL层 → 硬件

2.5 数据流的核心:SensorEventQueue

这里有个细节很多人不知道。应用层和系统服务层之间,数据是通过 SensorEventQueue 传递的。它是一个共享内存队列,系统服务层往里面写数据,应用层从里面读数据。这样做的好处是:

  • 减少Binder调用次数:不用每次数据变化都跨进程通信
  • 批量处理:可以一次读取多个事件
  • 降低延迟:共享内存比Binder快得多

我记得有一次排查距离传感器延迟问题,发现是 SensorEventQueue 的缓冲区太小,导致数据丢失。后来调整了 maxBatchReportLatency 参数才解决。

2.6 总结:记住这三层

好了,这一章的内容就这些。你只要记住:

  • 应用层:你写代码的地方,用SensorManager注册监听
  • 框架层:系统服务SensorService,负责调度和分发
  • HAL层:硬件厂商的驱动实现,把物理信号变成数字

这三层缺一不可。下一章我们会深入HAL层,看看距离传感器的驱动到底是怎么写的。到时候我会拿一个真实的驱动代码来拆解,保证你看完就能上手。

一句话总结:Android传感器框架就是一条从硬件到应用的数据管道,每一层都有自己的职责。理解了这个架构,你才能知道问题出在哪一层。

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