本文将介绍Android设备中的传感器。
大部分Android设备内置了大量的传感器,比较常见的有测量位移的、感应方向的、感应周围环境变化的 等等。这些传感器都能反馈高精度的原始数据、而且可以监测设备在三个方向上的位移和周围环境的变化 等。比如,在一款天气应用程序中可以使用温度传感器和湿度传感器得到周围环境的变化;在一款旅游应用程序中可以使用地磁传感器和加速器设定指南针的方向。
Android设备提供三种类型的传感器:
运动传感器(Motion sensors):这类传感器可以监测设备在三个坐标轴所在方向上的加速力和旋转力( measure acceleration forces and rotational forces along three axes)。主要有:加速计(accelerometers)、重力传感器(gravity sensors)、陀螺仪( gyroscopes)和旋转向量传感器( rotational vector sensors)等。
环境传感器(Environmental sensors):这类传感器可以监测设备所在的环境参数,如温度、气压、亮度、湿度 等(measure various environmental parameters, such as ambient air temperature and pressure, illumination, and humidity)。主要有:气压计(barometers)、光度计(photometers)、温度计(thermometers)等。
位置传感器(Position sensors):这类传感器可以监测设备所在物理位置。如方向传感器和磁力仪(orientation sensors and magnetometers)。
使用Android中的API可以轻松获取各类传感器所检测的原始数据。例如,您可以通过这些API做以下事:
当前设备中所包含的传感器数量和类别;
每一个传感器的参数。如可测量的最大值、生产商、所需功率、测量精确度 等( maximum range, manufacturer, power requirements, and resolution)。
从传感器中获取数据的间隔(频率)。
当传感器的状态发生变化时,可以通过注册监听器的方式监听。
从底层驱动划分,传感器还可以分为硬件驱动型和软件驱动型。硬件驱动的传感器(Hardware-based sensors)是内嵌于Android设备中的实实在在的物理部件。这些物理部件通过自身可以检测周围的环境属性,并输出测量数据,如加速度、地磁场强、角度变化 等。尽管由软驱动的传感器(Software-based sensors)看起来与硬件驱动的传感器高度相似,但前者并不是物理部件。软件驱动的传感器提供的数据通常是由若干个不同类型的硬件驱动的传感器的输出结果经过计算合成的,所以有时也将它们称作虚拟传感器或者合成传感器( virtual sensors or synthetic sensors)。线性加速器和重力传感器就属于软驱动传感器。下面的表格对Android各类传感器作了简要介绍。
在一个设备中,同一种类型的传感器,可能有多个。比方说,一个设备中含有两个重力传感器,每个传感器用于测量不同的范围。
您可以通过Android传感器框架获取这些传感器实例以及返回的监测参数。这面介绍的这些类或是接口,都在android.hardware包中。
SensorManager:您可以通过这个类获取传感器服务的实例。该类提供了获取不同种类传感器实例的方式、绑定和解除绑定监听传感器的方式、以及获取设备朝向参数的方式(This class provides various methods for accessing and listing sensors, registering and unregistering sensor event listeners, and acquiring orientation information)。该类还提供了若干静态常量,用于控制传感器的监测精度和返回数据的频率,以及校准方式(This class also provides several sensor constants that are used to report sensor accuracy, set data acquisition rates, and calibrate sensors)。
Sensor:通过该类可以获取指定的传感器实例。 通过这个实例,您可以调用大量方法来操作传感器。
SensorEvent:通过这个类,可以创建一个传感器事件的实例,该实例包含了这些信息:传感器的原始数据、生成的传感器事件的类型、数据的精度、事件的时间戳( the raw sensor data, the type of sensor that generated the event, the accuracy of the data, and the timestamp for the event)。
SensorEventListener:这个回调接口包含两个方法,当传感器监测的参数或精度发生变化时,这两个方法用于接收传感器事件返回的通知。
一般在应用中使用以上API的典型方式为:
确定传感器的类型以及传感器的功能(Identifying sensors and sensor capabilities):如果在您的应用程序中,某个功能需要通过特定传感器的特定功能来实现,那么检测设备中的传感器并寻找合适的类型并使用其特定功能就显得很有必要。
监测传感器事件(Monitor sensor events):监测传感器事件,即如何获取传感器的原始数据。当监测数据发生变化时,传感器事件都会回传最新的数据。这些数据包含四部分:触发事件的传感器的名字、事件的时间戳、事件的精确度、出发事件的原始数据( the name of the sensor that triggered the event, the timestamp for the event, the accuracy of the event, and the raw sensor data that triggered the event)。
不同的设备、不同的Android版本都具有不同的传感器。下面的这些传感器是在Android 4.0及以上版本中可用的传感器(有些已被废弃但仍然可用):
TYPE_ACCELEROMETER;
TYPE_AMBIENT_TEMPERATURE;
TYPE_GRAVITY;
TYPE_GYROSCOPE;
TYPE_LIGHT;
TYPE_LINEAR_ACCELERATION;
TYPE_MAGNETIC_FIELD;
TYPE_ORIENTATION(已被废弃但仍然可用);
TYPE_PRESSURE;
TYPE_PROXIMITY;
TYPE_RELATIVE_HUMIDITY;
TYPE_ROTATION_VECTOR;
TYPE_TEMPERATURE(已被废弃但仍然可用)。
获取SensorManager实例的方式如下:
private SensorManager mSensorManager; ... mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);获取当前设备中所有的传感器列表的方式如下:
List<Sensor> deviceSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);若您需要获取某一种特定类型的所有传感器列表,那么需要将TYPE_ALL常量替换为:
Sensor.TYPE_GYROSCOPE:这表示您需要获取所有具备陀螺仪功能的传感器列表;
Sensor.TYPE_LINEAR_ACCELERATION:这表示您需要获取所有具备线性加速器功能的传感器列表;
Sensor.TYPE_GRAVITY:这表示您需要获取所有具备重力仪功能的传感器列表;
您还可以调用getDefaultSensor()方法来判断设备中是否具有指定类型的传感器,若具有至少一个,那么方法将不返回空,否则将返回空,方式如下:
private SensorManager mSensorManager; ... mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); if (mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){ // 设备中至少内置了一个磁力计 } else { // 设备中没有内置磁力计 }除此之外,通过调用Sersor类中公有方法,您还可以为指定的传感器设置参数以决定其属性。比如,调用getResolution()和getMaximumRange()方法可以分别获取传感器的回传原始数据的频率和测量范围。调用getPower()方法可以获取传感器工作的额定功率。
通过调用Sensor类的getVendor()和getVersion()方法,可以获取设备中指定生产厂商和指定版本的传感器。假如您需要监测用户对设备的倾斜或摇动手势( tilt and shake),那么您需要使用重力仪,下面的例子演示了“在设备的所有重力仪中,获取生产厂商为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()返回一个整数,单位是微秒(microseconds),表示传感器回传原始数据的间隔,若返回的值不是0,表示这是一个流传感器( streaming sensor),流传感器说的是:不论传感器回传的原始监测数据有没有变化,传感器均按照这个间隔回传数据。相反若getMinDelay()返回0,表示不是流传感器,非流传感器表示只有监测的数据发生变化时,传感器才回传数据。
为了监听传感器回传的原始数据,您需要实现SensorEventListener接口中的两个回调方法:onAccuracyChanged() 和 onSensorChanged()。当下列事件发生时,系统会回调这两个方法:
当传感器的精度发生变化时(A sensor’s accuracy changes):在这种情况下,onAccuracyChanged()被回调,并回传一个Sensor类对象的引用,以调整传感器的精度。精度分为四个等级:SENSOR_STATUS_ACCURACY_LOW、SENSOR_STATUS_ACCURACY_MEDIUM、SENSOR_STATUS_ACCURACY_HIGH、SENSOR_STATUS_UNRELIABLE。
传感器回传了新的原始数据时(A sensor reports a new value):这时,onSensorChanged()被回调。并回传一个SensorEvent 类对象的引用,该对象包含了最新的原始数据,它包括:数据的精度、产生数据的传感器、数据产生的时间戳、新的数据 等( the accuracy of the data, the sensor that generated the data, the timestamp at which the data was generated, and the new data that the sensor recorded)。
下面演示了监听光照传感器的原始数据,并将数据显示在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表示回传数据的频率是200,000微秒( 0.2秒);其他的常量还有SENSOR_DELAY_GAME,表示间隔20,000微秒( 0.02秒);SENSOR_DELAY_UI,表示间隔60,000 微秒(0.06秒);SENSOR_DELAY_FASTEST,表示最快。
在Mainfest.xml中配置<uses-feature>标签,表示您的应用需要指定的传感器才能运行,如果某台设备未携带您指定的传感器,那么您的应用将不会出现在该设备所登陆的Google Play应用商店中。比如:
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />在<uses-feature>标签中配置了加速器,并将android:required属性设为true,这表示您的应用将完全依赖于加速计传感器才能运行,若设备中不包含加速计,那么使用该设备登陆Google Play时,您将无法搜索到该应用;若将android:required属性设为false,表示应用的部分功能需要使用加速计,但整个应用不依赖于加速计,在这种情况下,您依然能在商店中搜索到该应用,但需要使用加速计的那部分功能无法使用。
Android中传感器的坐标系如下图所示:
需要注意的是,手机的默认方向是竖屏的,也就是说,此时y轴是竖向的,而平板电脑的默认方向是横屏的,此时y轴也是竖直向上的(垂直于宽边)。
不要在模拟器上测试传感器(Don’t test your code on the emulator);
不要在onSensorChanged()方法中做大量操作(Don’t block the onSensorChanged() method):onSensorChanged()方法需要以较高的频率不断回传数据,所以不要阻塞该方法;
避免使用废弃的方法和常量(Avoid using deprecated methods or sensor types):TYPE_ORIENTATION这种类型的传感器已被废弃,欲获取方向,应调用getOrientation()方法;同样,TYPE_TEMPERATURE常量已被废弃,您应当使用TYPE_AMBIENT_TEMPERATURE常量替代。
在使用传感器之前一定要核实(Verify sensors before you use them):任何传感器都不是现成提供好的,需要您自己实例化;
谨慎设定传感器数据的回传频率 (Choose sensor delays carefully):当频率较高时,比较耗电。