Android官方文档
大多数Android设备都内置有测量运动,方向和各种环境情况的传感器,这些传感器能够提供多种高精度的原始数据。如果你想监控三维的设备运动或位置,或者是监控周围环境的变化,这些数据都是非常有用的。举个例子,我们玩一款游戏,它可能需要读取中立传感器的数据,以便去推断我们复杂的姿势和动作,像倾斜,振动,旋转或者振幅等情况。同样地,一款天气应用程序,可能需要用到设备的温度和湿度传感器,通过它们的数据去计算“结露点”。再者,一款旅行的应用软件可能需要使用磁感应和加速度传感器来计算罗盘的指针情况。
Android平台支持三大类传感器:
你可以通过使用Android Framework暴漏的API,访问设备上可用传感器的原始数据。Sensor Framework提供了一系列类和接口来帮助你执行各种Sensor相关的工作,例如,我们能够使用sensor framework做 如下一系列工作:
注册和注销监听sensor事件变化的listener。
该主题提供了在Android平台上传感器的概况,同时也介绍sensor framework的情况。
Android sensor framewrok允许你访问多种类型的传感器,这些传感器有的基于硬件,有一些是基于软件的。基于硬件的传感器是内嵌到手机或者平板中的物理器件,它们的数据直接来源于测量特定的物理特性,如加速度,地球磁场强度,或角速度的变化。基于软件的传感器没有物理元器件,它们是模仿基于硬件的传感器工作的。基于软件的传感器从一个或更多的基于硬件的传感器中获取数据。有时候它们也被称为虚拟传感器或合成传感器,线性加速度传感器和重力传感器就是基于软件传感器的例子。表1总结了Android平台支持的传感器。
一般的Android都不支持所有类型的传感器。例如,大部分手机和平板都有一个加速传感器和磁力传感器,但是很少有拥有气压传感器或者温度传感器。并且,一个设备可以拥有一种类型的不止一个的传感器,如,一个设备拥有两个重力传感器,每个传感器有不同的测量范围。
Table 1. Sensor types supported by the Android platform.
表1. Android平台支持的传感器类型
传感器 | 类型 | 描述 | 常见用法 |
---|---|---|---|
TYPE_ACCELEROMETER | 硬件 | 以m/s2为单位测量三个轴向的(x,y,和z)加速度,包括重力。 | 运动检测(震动,倾角等)。 |
TYPE_AMBIENT_TEMPERATURE | 硬件 | 以摄氏度为单位测量周围空间的温度,参阅下面的注释。 | 检测空气温度。 |
TYPE_GRAVITY | 软件/硬件 | 以m/s2为单位测量三个轴向的(x,y,和z)的重力。 | 运动检测(震动,倾角等)。 |
TYPE_GYROSCOPE | 硬件 | 以rad/s为单位测量设备三个物理轴线方向(x,y,和z)的旋转速度。 | 检测旋转(旋转,翻转等等)。 |
TYPE_LIGHT | 硬件 | 以lx为单位测量周围的光线级别(光照度)。 | 控制屏幕亮度。 |
TYPE_LINEAR_ACCELERATION | 软件或硬件 | 以m/s2为单位测量三个轴向的(x,y,和z)的加速度,不包括重力。 | 检测单个轴线的加速度。 |
TYPE_MAGNETIC_FIELD | 硬件 | 以 μT为单位测量周围三个物理轴向(x, y, z)的磁场。 | 创建一个罗盘。 |
TYPE_ORIENTATION | 软件 | 测量设备所有三个物理轴向(x,y和x)的旋转角度。当使用Level 3的API的时候,你能通过使用重力传感器和磁场传感器,结合getRotatinMatrix()方法,获取设备的倾斜矩阵和旋转矩阵。 | 检测设备的位置。 |
TYPE_PRESSURE | 硬件 | 以hPa或mbar为单位测量周围的气压。 | 检测气压的变化。 |
TYPE_PROXIMITY | 硬件 | 以cm为单位测量一个物体相对于设备屏幕的临近程度,这种传感器的典型用例是检测是否手机被放到人的耳旁。 | 检测通话过程中手机的位置。 |
TYPE_RELATIVE_HUMIDITY | 硬件 | 以百分比(%)为单位测量周围的相对湿度。 | 检测结露点,绝对和相对湿度。 |
TYPE_ROTATION_VECTOR | 软件或硬件 | 通过提供设备的三个旋转矢量测量设备方向。 | Motion detection and rotation detection. |
TYPE_TEMPERATURE | 硬件 | 以摄氏度为单位测量设备的温度。这个传感器在不同设备实现不同,并且这其在API Level 14中被TYPE_AMBIENT_TEMPERATURE替代。 | 检测温度。 |
通过Android sensor framework,你能访问这些传感器并获取它们的原始数据。sensor framework是android.hardware包的一部分,包含下面的一些类和接口:
在一个典型的应用程序中,使用这些传感器相关的API执行两个基本的任务:
如果你的应用程序有功能依赖特定类型的传感器及其功能,在运行时识别传感器及其功能是非常有用的。例如,你可能想识别设备上可用的所有传感器,和禁用所有依赖不存在的传感器的应用程序功能。同样,你可能想识别一个指定类型的所有传感器,以便你能选择某个传感器为你的应用程序实现最佳的性能。
传感器的可用性随着设备的不同而不同,也随着Android不同的版本而变化。这是因为Android的传感器历经几个平台版本逐步引入的。例如,一些传感器在Android1.5(API Level 3)中被引入,但是一些直到Android2.3(API Level 9)也没有被实现。同样的,多个传感器在Android2.3(API Level 9)和Anroid4.0(API Level 14)被引入。两个传感器被弃用,被更新的,更好的替代。
表2概括了每个传感器在每个平台的可用性。仅有四个平台被列出,这是因为传感器在这几个平台中发生了变化。列出的弃用传感器仍可在后续的平台使用(设备上存在这些传感器),这符合Android向前兼容的策略。
表2.传感器平台的可用性
Sensor | Sensor 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 |
1这个类型的传感器在Android1.5(API Level 3)被添加,但是直到Android2.3(API Level 9)也不可用。
2这个传感器是可用的,但是被弃用。
Android sensor framework提供了许多方法,它使你的运行时确定设备上有哪些传感器变得很容易。这个API也提供让你确定传感器的性能的方法,例如它的最大范围,它的分辨率,和它的功率要求。
为了识别在设备上的传感器,你首先需要获取传感器服务的引用。为此,需要通过调用getSystemService()方法并传递SENSOR_SERVICE参数,创建SensorManager类的一个实例。例如:
private SensorManager mSensorManager;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
接下来,通过调用getSensorList()方法,并使用TYPE_ALL常量获取设备上所有传感器列表。例如:
List 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()方法类获取传感器的功率需求。
如果你想针对不同厂商或者不同版本的传感器来优化你的应用程序,有两个公共方法是非常有用的。例如,如果你的应用程序需要检测用户的手势,例如倾斜和晃动,你能够创建一个数据过滤规则集合,针对较新的有指定厂商的重力传感器的设备进行优化。创建其它的数据过滤规则,针对没有重力传感器但仅有一个加速度传感器的设备进行优化。下面的代码片段展示了如何通过使用getVendor()和getVersion()方法来这样做。在这个例子中,我们查找一个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 gravSensors = mSensorManager.getSensorList(Sensor.TYPE_GRAVITY);
for(int i=0; iif ((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()方法返回非零值的传感器是流式传感器。流式传感器以规律的时间间隔采集数据,其在Andriod2.3(API Level 9)中被引入。如果调用getMinDelay()方法时传感器返回0,它意味着这个传感器不是一个流式传感器,因为它仅当感应的参数改变的时候才报告数据。
getMinDelay()方法是非常有用的,因为它让你确定了传感器获取数据最大的速率。如果在你应用程序中的某一个功能需要高数据采集率或者一个流式传感器,你能使用这个方法类确定是否这个传感器满足这些要求,然后启动或禁止相关功能。
注意:传感器的最大数据采集率并不一定是sensor framework传给应用程序数据的速率。sensor framework通过传感器事件上报数据,有多种因素会影响应用程序获取传感器事件的速率。更多信息查阅监测传感器事件。
为了监测原始数据,需要实现两个通过SensorEventListener接口暴漏的回调方法: onAccuracyChanged()和onSensorChanged(),当下面的事情发生时,Android系统会调用这两个回调函数:
下面的代码展示了如何通过使用onSensorChanged()方法来监控光线传感器的数据。这个例子在一个在main.xml文件中以sensor_data被定义的TextView中,用来显示了原始的数据。
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()回调方法向你的应用程序发送传感器事件的时间间隔。默认的数据间隔适合检测典型的屏幕方向改变,其值为200, 000微秒。你能指定其它的数据延迟,如SENSOR_DELAY_GAME(20, 000微秒延迟),SENSOR_DELAY_UI(60, 000微秒延迟),或者SENSOR_DELAY_FASTEST(0微秒延迟)。在Android3.0(API Level 11)中你也可以指定一个绝对的数值(以微秒)。
你指定的延迟仅仅是一个建议延迟。Android系统和其它应用系统以后可以改变这个延迟。最好的方法,你应该指定一个你能接受的最大的延迟,因为系统通常会使用一个比你指定的小的延迟(也就是说你应该选择一个满足你应用程序需要的最慢的采样率)。使用更大的延迟会 使处理器有更小的负载,因此有更低的能耗。
没有公共的方法来去决定sensor framework向你的应用程序发送传感器事件的速率;然而,你可以通过几个上报给你的事件的时间戳计算出采样率。一旦设置了采样率,就不能再改变采样率(延迟)。如果由于一些原因,你必须改变这个延迟,你将不得不注销并重新注册传感器的listener。
同样重要的是要注意这个例子中,使用onResume()和onPause()回调方法来注册和注销传感器事件的listener。最为一项最好实践,你应该总是在你不需要的时候禁用传感器,尤其是当你的Activity被Pause的时候。如果没有这样做,可能会导致在短短几个小时之内耗尽电池,因为一些传感器有很大的功率要求,并且会很快耗完电池。当屏幕关闭的时候系统不会会自动禁用传感器。
Android没有为设备指定一个标准的传感器配置,这意味着设备厂商可以将任何它们想要的传感器配置安装到他们的的Android设备。结果,多种范围的传感器可能会被内置到设备中。例如,Motorola Xoom有一个压力传感器,但是Samsung Nexus S没有这个传感器。同样,Xoom和Nexus S有陀螺仪传感器,但是HTC 的Nexus One则没有。如果你的应用程序依赖于特定类型的传感器,你必须确保这个传感器在设备上存在,从而能让你的应用程序成功运行。你有两个选择来确保特定的传感器在设备上存在:
下面的章节将分别讨论每一个选项。
如果你的应用程序使用一个特定类型的传感器,但是不依赖它,你能使用sensor framework在运行时检测传感器,然后酌情禁用和启动应用程序功能。例如,一个导航引用程序可能使用温度传感器,压力传感器,GPS传感器,和磁场传感器来显示温度,气压,位置和罗盘方位。如果设备没有一个压力传感器,你能使用sensor framework在运行时检查压力传感器是否存在,然后就可以在你的应用程序不显示压力的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中发布你的应用程序,如果你能在你的manifest文件中使用 <uses-feature> 元素来过滤掉那些没有你的应用程序需要的传感器配置的设备。<uses-feature>元素有多个硬件描述符,让你基于是否存在指定的传感器来过滤掉应用程序。能够列举的传感器包括:加速度,气压,罗盘(磁场),陀螺仪,光线,和距离传感器。下面是一个manifest的示,用来过滤没有加速度传感器的应用程序。
<uses-feature android:name="android.hardware.sensor.accelerometer"
android:required="true" />
如果需要向你的manifest文件中添加这个元素和描述符,只有当他们的设备有加速度传感器时才能在Google Play中看到你的应用程序。
只有当你的应用程序完全依赖一个特定的传感器的时候,你才应该设置这个android:required=”true”这个描述符。如果你的应用程序的一些功能使用这样一个传感器,但是没有这个传感器时仍然可以运行,你应该在元素中列出这个传感器,但是设置这个描述符为android:required=”false”。这将帮助确保设备能安装你的应用,即使它们没有这个特定的传感器。这也是一个项目管理的最佳实践,它能够帮组你跟踪你的应用程序使用的特性。记住,如果你的应用程序使用一个特定的传感器,但是没有它仍然可以运行,那么你应该在运行时检测这个传感器,并且酌情启动或禁用应用程序的某些功能。
通常情况下,sensor framework使用一个标准的3维坐标系来表达数据值,对于大多数传感器来说,当设备放置在默认的方向(图1所示)的时候,坐标系和设备的屏幕相关。当设备放置为它默认的方向,X轴是水平的并指向右边,Y轴是竖直并指向上方,并且Z轴指向屏幕面的外侧。在这个坐标系统,屏幕下方的坐标有负的Z值。这个坐标系被用于下面的传感器:
理解这个坐标表系的最重要的一点是,当设备屏幕的方向改变的时候,轴不改变—换句话说,传感器的坐标系不会随着当设备移动的而改变,这和OpenGL坐标系是相同的。
理解的另一点是你的应用程序不能假定设备的自然(默认)方向是竖屏。对于许多平板设备来说,他们的自然方向是横屏,并且传感器的坐标系总是基于设备的自然方向。
最后,如果你的应用程序匹配传感器数据要显示到屏幕上,你需要使用getRotation()方法来确定屏幕的旋转,然后使用remapCoordinateSystem()方法来映射传感器坐标系到屏幕坐标系。即使你的manifest文件指定了仅仅竖屏幕显示,你也需要这样做。
For more information about the sensor coordinate system, including information about how to handle screen rotations, see One Screen Turn Deserves Another.
更多关于传感器坐标系的信息,包含如何处理屏幕旋转的信息,查阅 One Screen Turn Deserves Another
注意:一些传感器和方法使用的坐标系是相对于真实世界的参照(相对于设备的参考框架)。这些传感器和方法返回数据代表设备的运动或者设备相对于地球的位置。更多信息,查阅getOrientation()方法,getRotationMatrix()方法,Orientation Sensor,和Rotation Vector Sensor。
当你设计你的传感器实现的时候,确保遵守这个章节下面讨论的准则。这些准则是为任何使用sensor framework访问并获取传感器数据的最佳实践。
当使用完成传感器或者当传感器activity pause的时候,确保注销传感器listener。如果一个传感器的listener被注册并且它的activity被pause,这个传感器将继续获取数据并且使用电池资源,除非你注销这个传感器。下面的代码展示了如何使用onPause()方法来注销一个listener:
private SensorManager mSensorManager;
...
@Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
更多的信息,请参阅 unregisterListener(SensorEventListener)。
你目前不能在模拟器上测试你的传感器代码,因为模拟器不能模拟传感器。你必须在一个物理设备上测试你的传感器代码。然而,有能够用来模拟传感器输出的传感器模拟器。
传感器数据可以高速的变化,这意味着系统可能经常调用onSensorChanged(SensorEvent)方法。作为最佳的实践,你应该竟尽可能少的在onSensorChanged(SensorEvent)方法中做事情,以便不去block这个回调。如果你的应用程序要求你做任何数据过滤或者减少传感器数据,你应该在onSensorChanged(SensorEvent)方法外执行这个工作。
一些方法和常量已经被弃用。尤其,TYPE_ORIENTATION类型的传感器已经被弃用。为了获取方向数据你应该使用getOrientation()方法来替代。同样,TYPE_TEMPERATURE传感器类型也已经被弃用,你应该在运行Andorid4.0的设备上使用TYPE_AMBIENT_TEMPERATURE传感器类型替代。
在你尝试从传感器获取数据之前,总是验证设备上是否存在这个传感器。不要因为它是一个常用的传感器而简单假设传感器存在。设备厂商没有被要求在它们的设备上提供任何指定的传感器。
当你使用registerListener()方法中注册一个传感器的时候,确保你选择一个适合你的应用程序或者用例的采集速率。传感器能高速地提供数据。允许系统发送额外的你不需要的数据,浪费系统资源并消耗电池电量。