Always make sure to disable sensors you don't need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours. Note that the system will not disable sensors automatically when the screen turns off.
确保在你不需要使用传感器的时候关掉它,特别是在 Activity 暂停的时候。如若不然,几个小时就会耗尽电池电量。请注意,系统在关闭屏幕的时候是不会自动停止传感器的。
Google APIs -- SensorManager

简介

智能手机经过近十年的发展,机身上集成了多种传感器(Sensor),一些传感器是基于硬件直接得出结果,另一些则是通过软件复合运算得出的。常见的重力计、陀螺仪以及距离感应计都属于硬件传感器,而方向计则是通过复合计算得出结果,属于软件传感器。

Sensor Type Description Common Uses
TYPE_ACCELEROMETER 硬件 测量设备在三个物理轴(x,y,z)上的加速度(m/s²),包括重力 运动检测(摇动,倾斜等)
TYPE_AMBIENT_TEMPERATURE 硬件 测量设备周围空间的摄氏温度(℃) 监测空气温度
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 or mbar) 检测气压变化
TYPE_PROXIMITY 硬件 测量一个物件到屏幕的距离(cm).该传感器通常用于确定手机是否靠近人耳 通话时的设备位置
TYPE_RELATIVE_HUMIDITY 硬件 测量周围的湿度(%) 检测露点,绝对和相对湿度
TYPE_ROTATION_VECTOR 软件或硬件 通过提供设备的三个旋转矢量元素确定设备的方向 运动和旋转检测

调用

获取传感器

要使用传感器,需要先获取手机的传感器服务。通过getSystemService创建一个SensorManager实例:

private SensorManager mSensorManager;...mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

接着,可以传入TYPE_ALL参数获取传感器列表:

List deviceSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);

如果你想获取指定类型的传感器列表,可以通过传入其他的类型常量,如TYPE_GYROSCOPE或者TYPE_GRAVITY来取代TYPE_ALL.
所有支持的传感器,并不会在所有的手机上都存在,类似于气压计这样的传感器,目前也只在高端机型上存在。所以在调取传感器的时候,需要对其做代码检查,例如

private SensorManager mSensorManager;...mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);if(mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){  //Success!存在磁力计}else{  //Failure!不存在磁力计}

监控传感器事件

要监控传感器事件,必须实现SensorEventListener接口的两个回调方法:onAccuracyChanged()onSensorChanged().

A sensor's accuracy changes.

传感器的精度变化会调用onAccuracyChanged()方法。精度有四个常量:SENSOR_STATUS_ACCURACY_LOW,SENSOR_STATUS_ACCURACY_MEDIUM,SENSOR_STATUS_ACCURACY_HIGHSENSOR_STATUS_UNRELIABLE

A sensor reports a new value.

传感器返回新的数据时候会调用onSensorChanged()方法,提供SensorEvent对象,该对象包含传感器的数据,包含数据精度,传感器,数据生成时间和传感器生成的数据。

接下来将通过一个指南针的例子来介绍传感器事件监听的运行机制。通过对 API 的查询了解,SENSOR_ORIENTATION已经在 API level 8废弃了,想要通过getDefaultSensor()方法直接获取方向参数已经不可取。官方给出的替代方法是一个静态(static)方法getOrientation(float[] R, float[] values).

getOrientation

基于旋转矩阵获取设备的方向。

请求参数
R float:旋转矩阵,通过getRotationMatrix(float[], float[], float[], float[])方法获取
values float:包含三个float的数组
返回参数
float[] 返回的数组

getRotationMatrix

getRotationMatrix(float[] R, float[] I, float[] gravity, float[] geomagnetic)
计算倾斜矩阵I和旋转矩阵R并将其通过矢量变换从设备的坐标系转换成真实世界正交坐标系:

  • X 定义为Y·Z的向量积(在设备所在的位置和地面相切,并大致指向东方)。
  • Y 在设备所在的位置与地面相切,并指向磁北极。
  • Z 指向天空并垂直于地面。


    正交坐标系

    其中

请求参数
R float:9个float参数的数组,保存旋转矩阵R的数据。R可以为空
I float:9个float参数的数组,保存倾斜矩阵I的数据。I可以为空
gravity float:3个float参数的数组,包含基于设备坐标系的重力矢量。使用类型为TYPE_ACCELEROMETER的传感器返回的数据
geomagnetic float:3个float参数的数组,包含基于设备坐标系的磁场矢量。使用类型为TYPE_MAGNETIC_FIELD的传感器返回的数据
返回参数
boolean true表示成功,false表示失败(当设备处于自由落体状态)。自由落体的界定,当设备承受的重力小于标称值的1/10时,视为自由落体。失败时,输出的矩阵值不会修改

构建一个指南针

在现实世界的正交坐标系中,磁北极是固定方向的。想要构造一个指南针应用,最直观的思考是获取到设备朝向和所处位置磁北极方向的夹角。这个时候,直观的思考就是正确的思考,如何获取这样一个夹角呢。通过上面介绍的 API 来看,getRotationMatrix()方法,能够获得设备的旋转矩阵和倾斜矩阵。旋转矩阵是一个33或者44的矩阵,通过getOrientation()方法就能直接获得设备关于正交系的三轴旋转数据。设备的指向必然是设备关于z轴的旋转角度。如何取值可以参考源码或者文档:

  • values[0]: Azimuth, angle of rotation about the -z axis. This value represents the angle between the device's y axis and the magnetic north pole. When facing north, this angle is 0, when facing south, this angle is π. Likewise, when facing east, this angle is π/2, and when facing west, this angle is -π/2. The range of values is -π to π.
  • values[1]: Pitch, angle of rotation about the x axis. This value represents the angle between a plane parallel to the device's screen and a plane parallel to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the top edge of the device toward the ground creates a positive pitch angle. The range of values is -π to π.
  • values[2]: Roll, angle of rotation about the y axis. This value represents the angle between a plane perpendicular to the device's screen and a plane perpendicular to the ground. Assuming that the bottom edge of the device faces the user and that the screen is face-up, tilting the left edge of the device toward the ground creates a positive roll angle. The range of values is -π/2 to π/2.

可知,此时此刻的values[0]正是我们想要的值。回归到代码的世界,是这样的:

public class SensorActivity extends Activity implements SensorEventListener{    //sensor object    private SensorManager mSensorManager;    private Sensor mAccelerometer;    private Sensor mMagneticField;    //sensor data    private float[] r = new float[9];   //rotation matrix    private float[] values = new float[3]   //orientation values    private float[] accelerometerValues = new float[3]  //data of acclerometer sensor    private float[] magneticFieldValues = new fooat[3]  //data of magnetic field sensor    @Ovrride    protected void onCreate(Bundle savedInstanceState) {        ...        //init sensor        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);            mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);            mMagneticField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);        ...    }            @Override        protected void onResume() {            super.onResume();        //regist listener            mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);            mSensorManager.registerListener(this, mMagneticField, SensorManager.SENSOR_DELAY_NORMAL);            ...        }    @Override        protected void onPause() {            super.onPause();        //unregist listener            mSensorManager.unregisterListener(this);        }    @Override        public void onSensorChanged(SensorEvent sensorEvent) {            if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {                    accelerometerValues = sensorEvent.values;            }            if (sensorEvent.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {                 magneticFieldValues = sensorEvent.values;            }            SensorManager.getRotationMatrix(r, null, accelerometerValues, magneticFieldValues);            SensorManager.getOrientation(r, values);            values[0] = (float) Math.toDegrees(values[0]);        }        @Override        public void onAccuracyChanged(Sensor sensor, int i) {        }}

整个过程是很流畅的,由于传感器本身是不受Activity的生命周期控制,并且系统并不会在关闭屏幕时关闭传感器,所以,请注意手动控制好传感器的开闭,以节省电量。

更多相关文章

  1. [RK3128][Android7.1]android鼠标按键板驱动模版
  2. android 重力感应手机方向
  3. Android传感器之距离传感器
  4. Sensor.TYPE_ORIENTATION 方向传感器,转向晕了个头
  5. Android(安卓)Alarm manager定时闹钟开发详解
  6. delphixe10 android操作 打电话,摄像头,定位等
  7. Android(安卓)ADB wifi 连接
  8. Android屏幕、键盘背光Framework和Linux led_classdev
  9. cling-java,android的协议栈

随机推荐

  1. 利用Xposed Hook打印Java函数调用堆栈信
  2. 【Jetpack系列一】Jetpack介绍
  3. the user data image is used by another
  4. Android(安卓)SystemClock 笔记
  5. delphi xe5 android 手机上使用sqlite
  6. Android消息机制简述(Java层)
  7. Android(安卓)view更改背景资源,padding消
  8. TSwitch 中文简繁显示支持(XE6 Android)
  9. openfire+asmack搭建的安卓即时通讯(四) 15
  10. Manifest.permission 这个类定义了androi