安卓Sensor分类

Android平台支持三大类传感器:

  • 动态传感器

    1. 这类传感器测量三个轴向上加速力和旋转力。这个类别中包含加速度计、重力传感器、陀螺仪和旋转矢量传感器。
  • 环境传感器

    1. 这类传感器测量各种环境参数,如环境气温、气压、照度和湿度。这个类别中包含气压计、光度计和温度计。
  • 位置传感器

    1. 这类传感器测量设备的物理位置。这个类别中包含屏幕方向传感器和磁力计。

    传感器框架概览

    安卓提供了一个传感器框架来访问设备上提供的传感器并获取原始数据。传感器框架提供多个类和接口,可用于执行各种与传感器相关的任务。例如可以使用传感器框架执行以下操作:

  • 确定设备上有哪些传感器

  • 确定单个传感器的特性,例如最大量程、制造商、功率要求和分辨率。
  • 获取原始传感器数据并定义获取传感器数据的最低频率。
  • 注册和取消注册用于监控传感器变化的传感器事件监听。

安卓支持的传感器

利用安卓的传感器框架,可以访问多种类型的传感器。有些传感器基于硬件,有些基于软件。基于硬件的传感器是内置在手机或平板设备中的物理组件。这类传感器通过直接测量特定的环境属性(如地磁场强度、角度变化或加速度)来采集数据。基于软件的传感器不是物理设备,它们只是模仿基于硬件的传感器。基于软件的传感器从一个或多个基于硬件的传感器获取数据,有时被称为虚拟传感器或合成传感器。比如重力传感器和线性加速度传感器就是基于软件的传感器。下表列出了安卓平台支持的传感器:

传感器 类型 说明 常见用途
TYPE_ACCELEROMETER 硬件 测量在所有三个物理轴向(x、y 和 z)上施加在设备上的加速力(包括重力),以 m/s 为单位。 动态检测(摇晃、倾斜等)。
TYPE_AMBIENT_TEMPERATURE 硬件 以摄氏度 (°C) 为单位测量环境室温。请参见下面的备注。 监测气温。
TYPE_GRAVITY 软件或硬件 测量在所有三个物理轴向(x、y、z)上施加在设备上的重力,单位为 m/s。 动态检测(摇晃、倾斜等)。
TYPE_GYROSCOPE 硬件 测量设备在三个物理轴向(x、y 和 z)上的旋转速率,以 rad/s 为单位。 旋转检测(旋转、转动等)。
TYPE_LIGHT 硬件 测量环境光级(照度),以 lx 为单位。 控制屏幕亮度。
TYPE_LINEAR_ACCELERATION 软件或硬件 测量在所有三个物理轴向(x、y 和 z)上施加在设备上的加速力(不包括重力),以 m/s 为单位。 监测单个轴向上的加速度。
TYPE_MAGNETIC_FIELD 硬件 测量所有三个物理轴向(x、y、z)上的环境地磁场,以 μT 为单位。 创建罗盘。
TYPE_ORIENTATION 软件 测量设备围绕所有三个物理轴(x、y、z)旋转的度数。从 API 级别 3 开始,您可以结合使用重力传感器、地磁场传感器和 getRotationMatrix() 方法来获取设备的倾角矩阵和旋转矩阵。 确定设备位置。
TYPE_PRESSURE 硬件 测量环境气压,以 hPa 或 mbar 为单位。 监测气压变化。
TYPE_PROXIMITY 硬件 测量物体相对于设备显示屏幕的距离,以 cm 为单位。该传感器通常用于确定手机是否被举到人的耳边。 通话过程中手机的位置。
TYPE_RELATIVE_HUMIDITY 硬件 测量环境的相对湿度,以百分比 (%) 表示。 监测露点、绝对湿度和相对湿度。
TYPE_ROTATION_VECTOR 软件或硬件 通过提供设备旋转矢量的三个元素来检测设备的屏幕方向。 动态检测和旋转检测。
TYPE_TEMPERATURE 硬件 测量设备的温度,以摄氏度 (°C) 为单位。该传感器的实现因设备而异。在 API 级别 14 中,该传感器已被 TYPE_AMBIENT_TEMPERATURE 传感器取代 监测温度。

表中标志传感器类别的字符串是com.hardware.Sensor类中的静态字符串常量,用于获取对应的传感器实例Sensor对象。

传感器框架

安卓传感器框架是androidd.hardwarer软件包的一部分,提供给应用开发者的类主要包含了以下类和接口:

SensorManager

  1. 使用该类来创建传感器服务的实例。该类提供了各种方法来访问和列出传感器,注册和取消注册传感器事件监听器,以及获取屏幕方向的信息。它还提供了几个传感器常量,用于报告传感器精确度,设置数据采集频率和校准传感器。它是一个抽象类,封装了一些传感器数据的交互函数和监听,其实现类为SystemSensorManager,对开发者不可见。<br /> 在安卓应用开发中,可以使用SensorManager及其相关接口来执行两个基本任务:
  • 识别传感器和传感器特性

如果应用具有依赖于特定传感器类型或特性的功能,则在运行时识别传感器和传感器特性非常有用。例如,您可 能希望识别设备上的所有传感器,以便于停用依赖于不存在的传感器的应用功能。同样,您可能希望识别特定类型的所有传感器,以便选择可以为应用带来最佳性能的传感器实现。
下面代码示例为android传感器框架识别传感器和传感器特性的代码

  1. private SensorManager sensorManager;

sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); List deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);

  1. - **监控传感器事件**
  2. ** **安卓应用开发者要实现监控原始传感器数据,需要实现两个通过SensorEventListener接口公开的回调方法:onAccuracyChanged()和onSensorChanged()。Android系统会在发生以下情况时调用这两个方法:<br />**1.传感器的准确度发生了变化。**<br />** ** 在这种情况下,系统会调用onAccuracyChange()方法,为开发者提供对于发生变化的Sensor对象的引用以及传感器的新准确度。准确度由以下 4 个状态常量之一表示:SENSOR_STATUS_ACCURACY_LOWSENSOR_STATUS_ACCURACY_MEDIUMSENSOR_STATUS_ACCURACY_HIGH SENSOR_STATUS_UNRELIABLE。<br />**2.传感器报告了新值**<br /> 在这种情况下,系统会调用 `onSensorChanged()` 方法,为您提供 `SensorEvent` 对象。`SensorEvent` 对象包含关于新传感器数据的信息,包括:数据的准确度、生成数据的传感器、生成数据的时间戳以及传感器记录的新数据。<br />下面代码示例展示了使用onSensorChanged()方法监控光传感器的数据

public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private Sensor mLight;

  1. @Override
  2. public final void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.main);
  5. sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
  6. mLight = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
  7. }
  8. @Override
  9. public final void onAccuracyChanged(Sensor sensor, int accuracy) {
  10. // Do something here if sensor accuracy changes.
  11. }
  12. @Override
  13. public final void onSensorChanged(SensorEvent event) {
  14. // The light sensor returns a single value.
  15. // Many sensors return 3 values, one for each axis.
  16. float lux = event.values[0];
  17. // Do something with this sensor value.
  18. }
  19. @Override
  20. protected void onResume() {
  21. super.onResume();
  22. sensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL);
  23. }
  24. @Override
  25. protected void onPause() {
  26. super.onPause();
  27. sensorManager.unregisterListener(this);
  28. }
  29. }
  1. <a name="4zvvC"></a>
  2. ### Sensor
  3. 您可以使用这个类来创建特定传感器的实例。该类提供了各种方法来确定传感器的特性。
  4. <a name="UHkkz"></a>
  5. ### SensorEvent
  6. 系统使用这个类来创建传感器事件对象,该对象提供有关传感器事件的信息。传感器事件对象中包含以下信息:原始传感器数据、生成事件的传感器类型、数据的准确度和事件的时间戳。
  7. <a name="qfkJX"></a>
  8. ### SensorEventListener
  9. 可以使用此接口创建两种回调方法,以在传感器值或传感器精确度发生变化时接收通知(传感器事件)。
  10. <a name="F4zyA"></a>
  11. ## 传感器坐标系
  12. 一般,安卓的传感器框架使用标准的3轴坐标系来表示数据值。当设备处于默认屏幕方向时,会相对于设备屏幕来定义坐标系(参见图 1)。当设备处于默认屏幕方向时,X 轴为水平向右延伸,Y 轴为垂直向上延伸,Z 轴为垂直于屏幕向外延伸。在此坐标系中,屏幕后面的坐标将具有负 Z 值。以下传感器使用此坐标系:<br />![axis_device.png](https://cdn.nlark.com/yuque/0/2020/png/505091/1605768163017-91ace820-f59e-45d0-9543-f57d4a2843aa.png#align=left&display=inline&height=269&margin=%5Bobject%20Object%5D&name=axis_device.png&originHeight=269&originWidth=225&size=11739&status=done&style=none&width=225)图1
  13. - 加速度传感器
  14. - 重力传感器
  15. - 陀螺仪
  16. - 线性加速度传感器
  17. - 地磁场传感器
  18. 此坐标系在设备的屏幕方向改变时,坐标轴不会转换,传感器的坐标系不会随着设备的移动而改变。
  19. <a name="9jXH3"></a>
  20. ### Android传感器框架Java层类图
  21. ![AndroidSensorFramework类图.png](https://cdn.nlark.com/yuque/0/2020/png/505091/1605843554152-6288966d-f8b9-4cb0-acea-8fa49377c5b8.png#align=left&display=inline&height=840&margin=%5Bobject%20Object%5D&name=AndroidSensorFramework%E7%B1%BB%E5%9B%BE.png&originHeight=840&originWidth=581&size=63242&status=done&style=none&width=581)
  22. <a name="3XXqk"></a>
  23. ### 对安卓开放的传感器框架API的总结
  24. 安卓传感器框架封装了各种Sensor数据的复杂处理,其数据处理主要在Native层,在Java层通过SystemSensorManager中的Native方法,调用相关数据处理方法,统一抽象为Sensor类,根据不同的标记值在SensorManager中获取相应的Sensor实例,使用观察者模式提供SensorEventListener接口让应用开发者监听需要的传感器数据变化,变化上报的数据统一抽象为SensorEvent类,其宗旨就是:封装了复杂的数据处理,提供简单统一的接口给开发者。在鸿蒙系统多模输入的Sensor框架设计提供了很好的借鉴示例。主要有以下几点:
  25. 1. 各种类型的传感器数据处理在Native层提供对应的类进行封装,数据处理转化为统一的数据(android的SensorEvent提供的就是一组Float数组)
  26. 1. 使用观察者模式,提供公共的数据变化接口给应用层,这也是安卓Framwork层最经典的方案。
  27. 1. 使用数值来标记各种传感器类型,将传感器统一封装为Sensor类,通过标记值来获取对应的传感器实例。
  28. 1. 提前考虑鸿蒙系统中传感器的坐标轴的问题,当然最简单的还是和安卓系统的设计保持一致,但仍需考虑是否有更优方案。
  29. 当然,仅仅分析安卓传感器框架设计了(或者说是提供了)怎样的API给开发者是远远不够的。它只是露出在海面上的冰山一角,海面下Native层的数据处理才是冰山的主要部分。
  30. <a name="Q0cVG"></a>
  31. ### Sensor框架 JNI方法列表
  32. android sensor框架Java层和Native层连接的类是SystemSensorService.java,通过里面的JNI方法,调用Native层的Sensor框架初始化方法进行Sensor扫描和初始化等动作,下面列出所有JNI方法:
  33. ```java
  34. private static native void nativeClassInit();
  35. private static native long nativeCreate(String opPackageName);
  36. private static native boolean nativeGetSensorAtIndex(long nativeInstance,Sensor sensor,int index);
  37. private static native void nativeGetDynamicSensors(long nativeInstance,List<Sensor> list);
  38. private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
  39. private static native int nativeCreateDirectChannel(long nativeInstance,long size,int channelType,int fd,HardwareBuffer buffer);
  40. private static native int nativeDestroyDirectChannel(long nativeInstance,long size,int channelType);
  41. private static native int nativeConfigDirectChannel(long nativeInstance,int channel,int sensorHandle,int rate);
  42. private static native int nativeSetOperationParameter(long nativeInstance,int type,float[] floatValues,int[] intValues);

BaseEventQueue中的Native方法:

  1. BaseEventQueueSystemSensorService的一个静态抽象类,它是Java层和Nativesensor service通讯的通道,SensorEventQueueTriggerEventQueue都是BaseEventQueue的子类,它们都是将各自的Listener监听和一组SensorEvent队列封装在一起。InjectEventQueue也是baseEventQueue的子类,它专门用于当数据注入Sensor 硬件抽象层时的回调,它不和某一个listener绑定,有且仅有一个InjectEventQueue绑定在SensorManager实例上。下面列出BaseEventQueueNative方法:
  1. private static native long nativeInitBaseEventQueue(long nativeManager,WeakReference<BaseEventQueue> eventQWeak,MessageQueue msgQ,String packageName.int mode,String opPackageName);
  2. private static native int nativeEnableSensor(long eventQ,int handle,int rateUs,int maxBatchReportLatencyUs);
  3. private static native int nativeDisableSensor(long envetQ,int handle);
  4. private static void nativeDestroySensorEventQueue(long envetQ);
  5. private static int nativeFlushSensor(long eventQ);
  6. private static int nativeInjectSensorData(long eventQ,int handle,float[] values,int accuracy,long timestamp);
  1. 其实,罗列出Java类中的native方法意义并不是很大,需要查看这些native方法在Java方法中哪些时机调用,结合native层对应的方法实现分析其调用目的,例如,nativeClassInit()方法,是在SystemSensorService类的构造方法,当判断到Natice类未被初始化时,进行调用,SystemSensorService中的native 方法 对应的实现类在Frameworks/base/core/jni/android_hardware_SensorManager.cpp中,<br />其主要将SensorListString类的结构映射绑定在相应的结构体上。而接下来在构造方法中又调用了nativeCreate方法,该方法主要创建了native层的SensorManager 该类在源码中的的文件路径为Frameworks/native/libs/sensor/SensorManager.cpp,而此类中引用该包路径下的其他类:<br />Frameworks/native/libs/sensor/Sensor.cpp<br />Frameworks/native/libs/sensor/ISensorServer.cpp<br />Frameworks/native/libs/sensor/SensorEventQueue.cpp<br />Frameworks/native/libs/sensor/ISensorEventConnection.cpp