大多数Android设备都具有测量运动,方向和各种环境条件的内置传感器。 这些传感器能够以高精度和精确度提供原始数据,如果要监视三维设备移动或定位,或者想要监视设备附近的环境环境中的变化,这些传感器很有用。 例如,游戏可以跟踪来自设备的重力传感器的读数,以推断复杂的用户手势和运动,例如倾斜,摇动,旋转或摆动。 同样,天气应用可能会使用设备的温度传感器和湿度传感器来计算和报告露点,或旅行应用程序可能会使用地磁场传感器和加速度计来报告罗盘方位。
Android平台支持三大类传感器:
1、运动传感器
这些传感器测量沿着三个轴的加速力和旋转力。 该类别包括加速度计,重力传感器,陀螺仪和旋转矢量传感器。
2、环境传感器
这些传感器测量各种环境参数,例如环境空气温度和压力,照明和湿度。 此类别包括气压计,光度计和温度计。
3、位置传感器
这些传感器测量设备的物理位置。 该类别包括定向传感器和磁力计。
您可以访问设备上可用的传感器,并使用Android传感器框架获取原始传感器数据。 传感器框架提供了几个类和接口,可帮助您执行各种传感器相关任务。 例如,您可以使用传感器框架执行以下操作:
1、确定设备上有哪些传感器可用。
2、确定单个传感器的功能,例如其最大范围,制造商,功率要求和分辨率。
3、获取原始传感器数据并定义获取传感器数据的最小速率。
4、注册和注销监控传感器更改的传感器事件侦听器。
本主题概述了Android平台上提供的传感器。 它还介绍了传感器框架。
一、传感器简介
Android传感器框架可让您访问许多类型的传感器。 这些传感器中的一些是基于硬件的,一些是基于软件的。 基于硬件的传感器是内置在手机或平板电脑设备中的物理组件。 它们通过直接测量特定的环境特性(如加速度,地磁场强度或角度变化)来得出数据。 基于软件的传感器不是物理设备,尽管它们模拟基于硬件的传感器。 基于软件的传感器从一个或多个基于硬件的传感器导出其数据,有时称为虚拟传感器或合成传感器。 线性加速度传感器和重力传感器是基于软件的传感器的示例。 表1总结了Android平台支持的传感器。
几乎没有Android设备具有每种类型的传感器。 例如,大多数手机设备和平板电脑都有加速度计和磁力计,但是更少的设备具有气压计或温度计。 此外,设备可以具有多于一个给定类型的传感器。 例如,设备可以具有两个重力传感器,每个重力传感器具有不同的范围。
表1. Android平台支持的传感器类型。
传感器 | 类型 | 描述 | 常见用途 |
---|---|---|---|
TYPE_ACCELEROMETER | 硬件 | 测量施加到所有三个物理轴(x,y和z)上的装置(包括重力)的m / s2加速度。 | 运动检测(摇动,倾斜等)。 |
TYPE_AMBIENT_TEMPERATURE | 硬件 | 测量环境温度(摄氏度)(℃)。 见下面的注释。 | 监测空气温度。 |
TYPE_GRAVITY | 软件或硬件 | 测量施加到所有三个物理轴(x,y,z)上的设备的m / s2中的重力。 | 运动检测(摇动,倾斜等)。 |
TYPE_GYROSCOPE | 硬件 | 测量三个物理轴(x,y和z)周围的设备旋转速度(rad / s)。 | 旋转检测(旋转,转动等)。 |
TYPE_LIGHT | 硬件 | 测量lx中的环境光线(照度)。 | 控制屏幕亮度。 |
TYPE_LINEAR_ACCELERATION | 软件或硬件 | 测量施加到所有三个物理轴(x,y和z)上的装置的m / s2的加速力,不包括重力。 | 沿单轴监测加速度。 |
TYPE_MAGNETIC_FIELD | 硬件 | 测量μT中所有三个物理轴(x,y,z)的环境地磁场。 | 创建指南针 |
TYPE_ORIENTATION | 软件 | 测量设备围绕所有三个物理轴(x,y,z)的旋转角度。 从API 3级起,您可以通过使用重力传感器和地磁场传感器以及getRotationMatrix()方法来获得设备的倾斜矩阵和旋转矩阵。 | 确定设备位置。 |
TYPE_PRESSURE | 硬件 | 测量hPa或mbar的环境空气压力。 | 监测气压变化。 |
TYPE_PROXIMITY | 硬件 | 测量相对于设备的视图屏幕的对象的接近度。 该传感器通常用于确定手持设备是否正在保持在人的耳朵上。 | 通话期间的电话位置。 |
TYPE_RELATIVE_HUMIDITY | 硬件 | 以百分比(%)测量相对环境湿度。 | 监测露点温度,绝对和相对湿度。 |
TYPE_ROTATION_VECTOR | 软件或硬件 | 通过提供设备旋转矢量的三个元素来测量设备的方位。 | 运动检测和旋转检测。 |
TYPE_TEMPERATURE | 硬件 | 以摄氏度(°C)测量设备的温度。 该传感器的实施方式因设备而异,此传感器已被API Level 14中的TYPE_AMBIENT_TEMPERATURE传感器所取代 | 监测温度 |
一)、传感器框架
您可以使用Android传感器框架访问这些传感器并获取原始传感器数据。 传感器框架是android.hardware软件包的一部分,包括以下类和接口:
您可以使用此类创建传感器服务的实例。 该类提供了访问和列出传感器,注册和注销传感器事件监听器以及获取方向信息的各种方法。 该类还提供了几个传感器常数,用于报告传感器精度,设置数据采集率和校准传感器。
您可以使用此类创建特定传感器的实例。 该类提供了各种可以确定传感器功能的方法。
系统使用此类创建传感器事件对象,该对象提供有关传感器事件的信息。 传感器事件对象包括以下信息:原始传感器数据,生成事件的传感器类型,数据的准确性以及事件的时间戳。
您可以使用此接口创建两个回传方法,当传感器值更改或传感器精度更改时接收通知(传感器事件)。
在典型的应用程序中,您可以使用这些与传感器相关的API执行两个基本任务:
1、识别传感器和传感器功能
如果您的应用程序具有依赖于特定传感器类型或功能,则在运行时识别传感器和传感器功能非常有用。 例如,您可能想要识别设备上存在的所有传感器,并禁用依赖于不存在的传感器的任何应用程序功能。 同样,您可能想要识别给定类型的所有传感器,因此您可以选择具有最佳性能的传感器实现。
2、监控传感器事件
监控传感器事件是如何获取原始传感器数据。 每当传感器检测到其测量参数的变化时,就会发生传感器事件。 传感器事件为您提供四条信息:触发事件的传感器的名称,事件的时间戳,事件的准确性以及触发事件的原始传感器数据。
二)、传感器可用性
虽然传感器可用性因设备而异,但Android版本也可能有所不同。 这是因为在几个平台发布的过程中引入了Android传感器。 例如,Android 1.5(API 3级)中引入了许多传感器,但有些没有实现,直到Android 2.3(API Level 9)才能使用。 同样,Android 2.3(API 9级)和Android 4.0(API等级14)中引入了几种传感器。 两个传感器已被弃用,并由更新更好的传感器代替。
表2总结了逐个平台的每个传感器的可用性。 只列出了四个平台,因为这些是涉及传感器变化的平台。 被列为已弃用的传感器仍然可以在后续平台上使用(传感器位于设备上),这符合Android的前向兼容性策略。
表2.平台的传感器可用性。
传感器 | Android 4.0 (API Level 14) | Android 2.3 (API Level 9) | Android 2.2 (API Level 8) | Android 1.5 (API Level 3) |
---|---|---|---|---|
TYPE_ACCELEROMETER | Yes | Yes | Yes | Yes |
TYPE_AMBIENT_TEMPERATURE | Yes | n/a | n/a | n/a |
TYPE_GRAVITY | Yes | Yes | n/a | n/a |
TYPE_GYROSCOPE | Yes | Yes | n/a1 | n/a1 |
TYPE_LIGHT | Yes | Yes | Yes | Yes |
TYPE_LINEAR_ACCELERATION | Yes | Yes | n/a | n/a |
TYPE_MAGNETIC_FIELD | Yes | Yes | Yes | Yes |
TYPE_ORIENTATION | Yes2 | Yes2 | Yes2 | Yes |
TYPE_PRESSURE | Yes | Yes | n/a1 | n/a1 |
TYPE_PROXIMITY | Yes | Yes | Yes | Yes |
TYPE_RELATIVE_HUMIDITY | Yes | n/a | n/a | n/a |
TYPE_ROTATION_VECTOR | Yes | Yes | n/a | n/a |
TYPE_TEMPERATURE | Yes2 | Yes | Yes | Yes |
注释2:该传感器可用,但已被弃用。
二、识别传感器和传感器能力
Android传感器框架提供了几种方法,可以方便您在运行时确定哪些传感器位于设备上。 API还提供了确定每个传感器功能的方法,例如其最大范围,其分辨率和功率要求。
要识别设备上的传感器,首先需要获取对传感器服务的参考。 为此,您可以通过调用getSystemService()方法并传入SENSOR_SERVICE参数来创建SensorManager类的实例。 例如:
private SensorManager mSensorManager;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
接下来,您可以通过调用getSensorList()方法并使用TYPE_ALL常量来获取设备上每个传感器的列表。 例如:
List<Sensor> deviceSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
如果要列出给定类型的所有传感器,可以使用另一个常量,而不是TYPE_ALL,例如TYPE_GYROSCOPE,TYPE_LINEAR_ACCELERATION或TYPE_GRAVITY。
您还可以通过使用getDefaultSensor()方法并传递特定传感器的类型常量来确定设备上是否存在特定类型的传感器。 如果设备具有多个给定类型的传感器,则必须将其中一个传感器指定为默认传感器。 如果给定类型的传感器不存在默认传感器,则方法调用返回null,这意味着该设备不具有该类型的传感器。 例如,以下代码检查设备上是否有磁力计:
private SensorManager mSensorManager;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){
// Success! There's a magnetometer.
}
else {
// Failure! No magnetometer.
}
注意:Android不需要设备制造商在其Android设备中构建任何特定类型的传感器,因此设备可以具有广泛的传感器配置。
除了列出设备上的传感器之外,还可以使用Sensor类的公共方法来确定各个传感器的功能和属性。 这是有用的,如果您希望应用程序根据设备上可用的传感器或传感器功能的不同而有所不同。 例如,您可以使用getResolution()和getMaximumRange()方法来获取传感器的分辨率和最大测量范围。 您还可以使用getPower()方法来获取传感器的电源要求。
如果要优化不同制造商的传感器或不同版本的传感器的应用程序,则两种公共方法特别有用。例如,如果您的应用程序需要监视用户手势,例如倾斜和摇动,则可以为具有特定供应商重力传感器的较新设备创建一组数据过滤规则和优化,以及另一组没有重力传感器,只有一个加速度计的数据过滤规则和设备优化。在这个示例中,我们正在寻找一个重力传感器,它将Google Inc.列为供应商,版本号为3.如果设备上不存在特定的传感器,我们尝试使用加速度计。
private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = null;
if (mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null){
List<Sensor> gravSensors = mSensorManager.getSensorList(Sensor.TYPE_GRAVITY);
for(int i=0; i<gravSensors.size(); i++) {
if ((gravSensors.get(i).getVendor().contains("Google Inc.")) &&
(gravSensors.get(i).getVersion() == 3)){
// Use the version 3 gravity sensor.
mSensor = gravSensors.get(i);
}
}
}
if (mSensor == null){
// Use the accelerometer.
if (mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
else{
// Sorry, there are no accelerometers on your device.
// You can't play this game.
}
}
另一个有用的方法是getMinDelay()方法,它返回传感器可以用来感测数据的最小时间间隔(以微秒为单位)。 任何为getMinDelay()方法返回非零值的传感器都是流式传感器。 流传感器定期检测数据,并在Android 2.3(API等级9)中引入。 如果在调用getMinDelay()方法时传感器返回0,则表示传感器不是流传感器,因为只有当传感器参数发生变化时才报告数据。
getMinDelay()方法很有用,因为它可以让您确定传感器可以获取数据的最大速率。 如果应用程序中的某些功能需要较高的数据采集速率或流式传感器,则可以使用此方法来确定传感器是否满足这些要求,然后相应地启用或禁用应用程序中的相关功能。
注意:传感器的最大数据采集速率不一定是传感器框架将传感器数据传送到应用程序的速率。 传感器框架通过传感器事件报告数据,并且几个因素会影响应用程序接收传感器事件的速率。 有关详细信息,请参阅监视传感器事件。
三、监控传感器事件
要监视原始传感器数据,您需要实现通过SensorEventListener接口公开的两个回调方法:onAccuracyChanged()和onSensorChanged()。 只要发生以下情况,Android系统就会调用这些方法:
1、传感器的精度变化。
在这种情况下,系统调用onAccuracyChanged()方法,提供对传感器对象的引用以及传感器的新精度。 精度由以下四种状态常数之一表示:SENSOR_STATUS_ACCURACY_LOW,SENSOR_STATUS_ACCURACY_MEDIUM,SENSOR_STATUS_ACCURACY_HIGH或SENSOR_STATUS_UNRELIABLE。
2、传感器报告一个新值。
在这种情况下,系统将调用onSensorChanged()方法,为您提供一个SensorEvent对象。 SensorEvent对象包含有关新传感器数据的信息,包括:数据的准确性,生成数据的传感器,数据生成的时间戳以及传感器记录的新数据。
以下代码显示了如何使用onSensorChanged()方法监视光传感器的数据。 此示例将在main.xml文件中定义的TextView中的raw sensor数据显示为sensor_data。
public class SensorActivity extends Activity implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mLight;
@Override
public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
@Override
public final void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do something here if sensor accuracy changes.
}
@Override
public final void onSensorChanged(SensorEvent event) {
// The light sensor returns a single value.
// Many sensors return 3 values, one for each axis.
float lux = event.values[0];
// Do something with this sensor value.
}
@Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
}
在此示例中,调用registerListener()方法时指定默认数据延迟(SENSOR_DELAY_NORMAL)。 数据延迟(或采样率)通过onSensorChanged()回调方法控制传感器事件发送到应用程序的时间间隔。 默认数据延迟适用于监视典型的屏幕方向更改,并使用延迟20万微秒。 您可以指定其他数据延迟,例如SENSOR_DELAY_GAME(20,000微秒延迟),SENSOR_DELAY_UI(60,000微秒延迟)或SENSOR_DELAY_FASTEST(0微秒延迟)。 从Android 3.0(API 11级)开始,您还可以将延迟指定为绝对值(以微秒为单位)。
您指定的延迟只是建议的延迟。 Android系统和其他应用程序可以改变这种延迟。 作为最佳实践,您应该指定最大的延迟时间,因为系统通常使用的延迟比您指定的延迟更小(也就是说,您应该选择仍然满足应用需求的最慢采样率)。 使用更大的延迟对处理器施加较低的负载,因此使用较少的功率。
没有公共方法来确定传感器框架向您的应用发送传感器事件的速率; 但是,您可以使用与每个传感器事件相关联的时间戳来计算几个事件的采样率。 设置完毕后,您不必更改采样率(延迟)。 如果由于某种原因您需要更改延迟,则必须注销并重新注册传感器侦听器。
同样重要的是要注意,此示例使用onResume()和onPause()回调方法来注册和注销传感器事件侦听器。 作为最佳做法,您应该始终禁用不需要的传感器,特别是当您的活动暂停时。 否则可能会在短短几个小时内耗尽电池,因为某些传感器具有很大的功率需求,并且可以快速耗尽电池电量。 当屏幕关闭时,系统将不会自动禁用传感器。
四、处理不同的传感器配置
Android没有为设备指定标准传感器配置,这意味着设备制造商可以将他们想要的任何传感器配置纳入其Android设备。 因此,设备可以包括各种各样的配置中的各种传感器。 如果您的应用程序依赖于特定类型的传感器,则必须确保传感器位于设备上,以便您的应用程序可以成功运行。
您有两个选项用于确保设备上存在给定的传感器:
1、在运行时检测传感器,并根据需要启用或禁用应用程序功能。
2、使用Google Play过滤器来定位具有特定传感器配置的设备。
以下各节将讨论每个选项。
一)、在运行时检测传感器
如果您的应用程序使用特定类型的传感器,但不依赖它,则可以使用传感器框架在运行时检测传感器,然后根据需要禁用或启用应用程序功能。 例如,导航应用程序可能会使用温度传感器,压力传感器,GPS传感器和地磁场传感器来显示温度,气压,位置和罗盘方位。 如果设备没有压力传感器,您可以使用传感器框架在运行时检测压力传感器的缺失,然后禁用显示压力的应用程序UI部分。 例如,以下代码检查设备上是否有压力传感器:
private SensorManager mSensorManager;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null){
// Success! There's a pressure sensor.
}
else {
// Failure! No pressure sensor.
}
二)、使用Google Play过滤器来定位特定的传感器配置
如果您要在Google Play上发布您的应用程序,您可以使用清单文件中的<uses-feature>元素从您的应用程序没有适当传感器配置的设备中过滤应用程序。 <uses-feature>元素有几个硬件描述符,可以根据特定传感器的存在过滤应用程序。 您可以列出的传感器包括:加速度计,气压计,罗盘(地磁场),陀螺仪,光和接近。 以下是过滤不具有加速度计的应用程序的示例清单条目:
<uses-feature android:name="android.hardware.sensor.accelerometer"
android:required="true" />
如果您将此元素和描述符添加到应用程序的清单中,用户只有在其设备具有加速度计时才会在Google Play上看到您的应用程序。
您应该将描述符设置为android:required =“true”,只有当您的应用程序完全依赖于特定的传感器。 如果您的应用程序使用传感器进行某些功能,但仍未运行传感器,则应在<uses-feature>元素中列出传感器,但将描述符设置为android:required =“false”。 这有助于确保设备可以安装您的应用程序,即使它们没有该特定的传感器。 这也是项目管理最佳实践,可帮助您跟踪应用程序使用的功能。 请记住,如果您的应用程序使用特定的传感器,但仍然没有传感器运行,那么您应该在运行时检测传感器,并根据需要禁用或启用应用程序功能。
五、传感器坐标系
通常,传感器框架使用标准的3轴坐标系来表示数据值。 对于大多数传感器,当设备保持默认方向时,相对于设备屏幕定义坐标系(见图1)。 当设备保持默认方向时,X轴为水平方向,指向右侧,Y轴为垂直方向并指向上方,Z轴指向画面的外侧。 在该系统中,屏幕后面的坐标具有负Z值。 以下传感器使用该坐标系:
1、加速度传感器
2、重力传感器
3、陀螺仪
4、线性加速度传感器
5、地磁场传感器
了解该坐标系最重要的一点是当设备的屏幕方向发生变化时轴不会交换,也就是传感器的坐标系从不随设备移动而改变。 此行为与OpenGL坐标系的行为相同。
另外要理解的一点是,您的应用程序不能假设设备的自然(默认)方向是纵向。 许多平板电脑的自然方向是景观。 传感器坐标系始终基于设备的自然方向。
最后,如果您的应用程序将传感器数据与屏幕显示匹配,则需要使用getRotation()方法来确定屏幕旋转,然后使用remapCoordinateSystem()方法将传感器坐标映射到屏幕坐标。 即使您的清单指定了仅纵向显示,您也需要执行此操作。
有关传感器坐标系的更多信息,包括有关如何处理屏幕旋转的信息,请参阅一个屏幕转换符合另一个。
注意:一些传感器和方法使用相对于世界参考系的坐标系(与设备的参考框架相反)。 这些传感器和方法返回表示相对于地球的设备运动或设备位置的数据。 有关更多信息,请参阅getOrientation()方法,getRotationMatrix()方法,方向传感器和旋转矢量传感器。
六、访问和使用传感器的最佳做法
在设计传感器实现时,请务必遵循本节中讨论的指导原则。 对于正在使用传感器框架访问传感器并获取传感器数据的任何人,都建议使用这些指南。
一)、用Android模拟器测试
Android模拟器包括一组虚拟传感器控制,可以让您测试传感器,如加速度计,环境温度,磁力计,接近度,光线等。
仿真器使用与运行SdkControllerSensor应用程序的Android设备的连接。 请注意,此应用仅适用于运行Android 4.0(API级别14)或更高版本的设备。 (如果设备正在运行Android 4.0,则必须安装版本2)SdkControllerSensor应用程序监视设备上传感器的更改并将其传输到仿真器。 然后根据从设备上的传感器接收到的新值,对仿真器进行转换。
您可以在以下位置查看SdkControllerSensor应用程序的源代码:
$ your-android-sdk-directory/tools/apps/SdkController
要在设备和仿真器之间传输数据,请按照下列步骤操作:
1、检查设备上是否启用了USB调试功能。
2、使用USB电缆将设备连接到开发机器。
3、在设备上启动SdkControllerSensor应用程序。
4、在应用程序中,选择要仿真的传感器。
5、运行以下adb命令:
$ adb forward tcp:1968 tcp:1968
6、启动模拟器。 您现在应该能够通过移动设备将转换应用于仿真器。
注意:如果您对物理设备进行的移动不会转换模拟器,请再次尝试从步骤5运行adb命令。
二)、不要堵塞onSensorChanged()方法
传感器数据可以以高速率进行更改,这意味着系统可能会经常调用onSensorChanged(SensorEvent)方法。 作为最佳做法,您应该在onSensorChanged(SensorEvent)方法中尽可能少做,因此您不要堵塞它。 如果您的应用程序要求您进行任何数据过滤或减少传感器数据,则应在onSensorChanged(SensorEvent)方法之外执行此工作。
三)、避免使用不推荐使用的方法或传感器类型
已经弃用了几种方法和常数。 特别地,TYPE_ORIENTATION传感器类型已被弃用。 要获取方向数据,您应该使用getOrientation()方法。 同样,TYPE_TEMPERATURE传感器类型已被弃用。 您应该在运行Android 4.0的设备上使用TYPE_AMBIENT_TEMPERATURE传感器类型。
四)、在使用传感器之前验证传感器
在尝试从其获取数据之前,请务必验证设备上是否存在传感器。 不要假设传感器存在,只因为它是一个常用的传感器。 设备制造商不需要在其设备中提供任何特定的传感器。
五)、仔细选择传感器延迟
当使用registerListener()方法注册传感器时,请确保选择适合您的应用程序或用例的传输速率。 传感器可以以非常高的速率提供数据。 允许系统发送不需要的额外数据浪费系统资源并使用电池供电。