今天我们开始进入讲解android中的一些高级主题的用法,比如传感器、GPS、NFC、语音和人脸识别等。
这次来对传感器的一个简单介绍:
Android平台支持三大类的传感器:
位移传感器
这些传感器测量沿三个轴线测量加速度和旋转。这类包含加速度,重力传感器,陀螺仪,和矢量传感器。
环境传感器
这些传感器测量各种环境参数,例如周围的空气温度和压力,光线,和湿度。这类包含气压,光线,和温度传感器。
位置传感器
这些传感器测量设备的物理位置。这类包含方向和磁力传感器。
这些传感器的一些是基于硬件的,一些是基于软件的。基于硬件的传感器是内嵌到手机或者平板中的物理元件,它们通过直接测量指定的环境属性来得到它们的数据,例如加速度,磁场强度,或者角度变化。基于软件的传感器不是物理设备,尽管它们模仿基于硬件的传感器。基于软件的 传感器从一个或更多基于硬件的传感器获取它们的数据,并且有时候被称为虚拟传感器或者合成传感器。线性加速度传感器和重力传感器是基于软件传感器的例子。
很少Android设备有所有类型的传感器。例如,大部分手机和平板有一个加速计和磁场计,但是很少的设备拥有气压或者温度传感器。并且,一个设备可以拥有一个类型不止一个的传感器。例如,设备能有两个重力传感器,每个有不同的范围。
需要指出的是,传感器的坐标系统与屏幕坐标系统不同,传感器坐标系统的X轴沿屏幕向右;Y轴则沿屏幕向上,Z轴在垂直屏幕向上。
我们依次看看几种传感器:
1 加速度传感器
加速度传感器又叫G-sensor,返回x、y、z三轴的加速度数值。
该数值包含地心引力的影响,单位是m/s^2。
将手机平放在桌面上,x轴默认为0,y轴默认0,z轴默认9.81。
将手机朝下放在桌面上,z轴为-9.81。
将手机向左倾斜,x轴为正值。
将手机向右倾斜,x轴为负值。
将手机向上倾斜,y轴为负值。
将手机向下倾斜,y轴为正值。
2 磁力传感器
磁力传感器简称为M-sensor,返回x、y、z三轴的环境磁场数据。
该数值的单位是微特斯拉(micro-Tesla),用uT表示。
单位也可以是高斯(Gauss),1Tesla=10000Gauss。
硬件上一般没有独立的磁力传感器,磁力数据由电子罗盘传感器提供(E-compass)。
电子罗盘传感器同时提供下文的方向传感器数据。
3 方向传感器
方向传感器简称为O-sensor,返回三轴的角度数据,方向数据的单位是角度。
为了得到精确的角度数据,E-compass需要获取G-sensor的数据,
经过计算生产O-sensor数据,否则只能获取水平方向的角度。
方向传感器提供三个数据,分别为azimuth、pitch和roll。
azimuth:方位,以z轴为轴,返回水平时磁北极和Y轴的夹角,范围为0°至360°。
0°=北,90°=东,180°=南,270°=西。
pitch:x轴和水平面的夹角,范围为-180°至180°。
当z轴向y轴转动时,角度为正值。
roll:y轴和水平面的夹角,由于历史原因,范围为-90°至90°。
当x轴向z轴移动时,角度为正值。
4 陀螺仪传感器
陀螺仪传感器叫做Gyro-sensor,返回x、y、z三轴的角加速度数据。
角加速度的单位是radians/second。
根据Nexus S手机实测:
水平逆时针旋转,Z轴为正。
水平逆时针旋转,z轴为负。
向左旋转,y轴为负。
向右旋转,y轴为正。
向上旋转,x轴为负。
向下旋转,x轴为正。
5 光线感应传感器
光线感应传感器检测实时的光线强度,光强单位是lux,其物理意义是照射到单位面积上的光通量。
光线感应传感器主要用于Android系统的LCD自动亮度功能。
可以根据采样到的光强数值实时调整LCD的亮度。
6 压力传感器
压力传感器返回当前的压强,单位是百帕斯卡hectopascal(hPa)。
7 温度传感器
温度传感器返回当前的温度。
8 距离传感器
距离传感器检测物体与手机的距离,单位是厘米。
一些距离传感器只能返回远和近两个状态,
因此,距离传感器将最大距离返回远状态,小于最大距离返回近状态。
距离传感器可用于接听电话时自动关闭LCD屏幕以节省电量。
一些芯片集成了距离传感器和光线传感器两者功能。
下面三个传感器做个比较:
重力传感器
重力传感器简称GV-sensor,输出重力数据。
在地球上,重力数值为9.8,单位是m/s^2。
坐标系统与加速度传感器相同。
当设备复位时,重力传感器的输出与加速度传感器相同。
线性加速度传感器
线性加速度传感器简称LA-sensor。
线性加速度传感器是加速度传感器减去重力影响获取的数据。
单位是m/s^2,坐标系统与加速度传感器相同。
加速度传感器、重力传感器和线性加速度传感器的计算公式如下:
加速度 = 重力 + 线性加速度
旋转矢量传感器
旋转矢量传感器简称RV-sensor。
旋转矢量代表设备的方向,是一个将坐标轴和角度混合计算得到的数据。
RV-sensor输出三个数据:
x*sin(theta/2)
y*sin(theta/2)
z*sin(theta/2)
sin(theta/2)是RV的数量级。
RV的方向与轴旋转的方向相同。
RV的三个数值,与cos(theta/2)组成一个四元组。
RV的数据没有单位,使用的坐标系与加速度相同。
传感器框架:
你能访问这些传感器,是通过使用Android传感器框架获取原始数据。Android传感器框架式android.hardware包的一部分,包含下面的类和接口:
SensorManager
你能使用这个类来创建一个传感器服务的实例。这个类提供了各种方法类访问和列举传感器,注册和注销传感器事件监听,并获取相应的信息。这个类也提供了几个传感器的常量,用户报告传感器的精确度,设置数据获取速率,和校准传感器。
Sensor
你能使用这个类来创建一个指定传感器的实例。这个类提供了各种方法让你确定传感器的功能。
SensorEvent
它提供了关于传感器事件的信息。一个传感器事件包含以下信息:原始传感器数据,这类传感器产生的事件,数据的准确性,和事件的时间戳。
SensorEventListener
你能使用这个接口来创建两个回调方法,当传感器的值改变或者当传感器的精度改变的时候,它接受通知(传感器事件)。
传感器的值改变
在这种情况下系统调用onSensorChanged()方法,向你提供了一个SensorEvent对象,一个SensorEvent对象包含关于新的传感器数据的信息,包括:数据的精度,传感器产生的数据,数据产生的时间戳,和传感器记录的新的数据。
传感器精度的变化
在 这种情况下系统调用onAccuracyChanged()方法,向你提供改变了新的传感器精度的Sensor对象引用。精度通过四个状态常量代 表:SENSOR_STATUS_ACCURACY_LOW,SENSOR_STATUS_ACCURACY_MEDIUM,SENSOR_STATUS_ACCURACY_HIGH, 或者SENSOR_STATUS_UNRELIABLE。
使用传感器的步骤如下:
①调用Context的getSystemService(Context.SENSOR_SERVICE)方法获取SensorManager对象。
②调用SensorManager的getDefaultSensor(int type)方法来获取指定类型的传感器。
从传感器管理器中获取其中某个或者某些传感器的方法有如下三种:
第一种:获取某种传感器的默认传感器
Sensor defaultGyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
第二种:获取某种传感器的列表
List<Sensor> pressureSensors = sensorManager.getSensorList(Sensor.TYPE_PRESSURE);
第三种:获取所有传感器的列表,我们这个例子就用的第三种
List<Sensor> allSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
③一般在Activity的onResume()方法中调用SensorManager的registerListener()为指定传感器注册监听器即可。程序可以通过实现监听器即可获取传感器传回来的数据。
SersorManager提供的注册传感器的方法为registerListener(SensorListener listener, Sensor sensor, int rate)该方法中三个参数说明如下:
listener:监听传感器事件的监听器
sensor:传感器对象
rate:指定获取传感器数据的频率
rate可以获取传感器数据的频率,支持如下几个频率值:
SENSOR_DELAY_FASTEST:最快,延迟最小。
SENSOR_DELAY_GAME:适合游戏的频率。
SENSOR_DELAY_NORMAL:正常频率
SENSOR_DELAY_UI:适合普通用户界面的频率。
那就来举个例子来测试下:
这个例子就是显示下手机上支持的传感器及传感器的一些信息并测试几个常用传感器
效果:
核心代码:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.fenxichuanganqi.MainActivity" >
<TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="方向传感器:" android:textColor="#ff0000" />
<TextView android:id="@+id/tv_direction" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="线性传感器:" android:textColor="#ff0000" />
<TextView android:id="@+id/tv_xianxing" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加速度传感器:" android:textColor="#ff0000" />
<TextView android:id="@+id/tv_jiasudu" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="光强传感器:" android:textColor="#ff0000" />
<TextView android:id="@+id/tv_guangqiang" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="距离传感器:" android:textColor="#ff0000" />
<TextView android:id="@+id/tv_juli" android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
MainActivity.java
public class MainActivity extends Activity implements SensorEventListener{
private TextView tv;
private TextView tv_direction;
private TextView tv_xianxing;
private TextView tv_jiasudu;
private TextView tv_guangqiang;
private TextView tv_juli;
private SensorManager sensorManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1.获取SensorManager服务
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
tv = (TextView) findViewById(R.id.tv);
tv_direction = (TextView) findViewById(R.id.tv_direction);
tv_xianxing = (TextView) findViewById(R.id.tv_xianxing);
tv_jiasudu = (TextView) findViewById(R.id.tv_jiasudu);
tv_guangqiang = (TextView) findViewById(R.id.tv_guangqiang);
tv_juli = (TextView) findViewById(R.id.tv_juli);
//获取手机上支持的传感器
List<Sensor> list = sensorManager.getSensorList(Sensor.TYPE_ALL);
tv.append("手机上有" + list.size() + "个传感器" + "\n");
for (Sensor sensor : list) {
String msg = "名字:" + sensor.getName() + ",版本:" + sensor.getVersion()
+",供应商:" + sensor.getVendor() + ",类型:" + sensor.getType();
tv.append(msg + "\n");
}
}
@Override
protected void onResume() {
//23.获得相应传感器并注册监听器
//第三个参数表示精度
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
sensorManager.SENSOR_DELAY_UI);
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION),
sensorManager.SENSOR_DELAY_UI);
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
sensorManager.SENSOR_DELAY_UI);
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),
sensorManager.SENSOR_DELAY_UI);
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
sensorManager.SENSOR_DELAY_UI);
super.onResume();
}
@Override
protected void onStop() {
//4.解除绑定
sensorManager.unregisterListener(this);
super.onStop();
}
@Override
public void onSensorChanged(SensorEvent event) {
//传感器数据变化,在该方法中我们可以获取传感器变化的值
switch (event.sensor.getType()) {
case Sensor.TYPE_ORIENTATION:
float z = event.values[0];
float x = event.values[1];
float y = event.values[2];
tv_direction.setText("z轴的方向:" + z + "\n"
+ "x轴的方向:" + x + "\n"
+ "y轴的方向:" + y + "\n");
break;
case Sensor.TYPE_LINEAR_ACCELERATION:
float x1 = event.values[0];
float y1 = event.values[1];
float z1 = event.values[2];
tv_xianxing.setText("x轴的加速度:" + x1 + "\n"
+ "y轴的加速度:" + y1 + "\n"
+ "z轴的加速度:" + z1 + "\n");
break;
case Sensor.TYPE_ACCELEROMETER:
float x2 = event.values[0];
float y2 = event.values[1];
float z2 = event.values[2];
tv_jiasudu.setText("x轴的加速度:" + x2 + "\n"
+ "y轴的加速度:" + y2 + "\n"
+ "z轴的加速度:" + z2 + "\n");
break;
case Sensor.TYPE_LIGHT:
float light = event.values[0];
tv_guangqiang.setText("光强:" + light);
break;
case Sensor.TYPE_PROXIMITY:
float distanse = event.values[0];
tv_juli.setText("距离传感器:" + distanse);
break;
default:
break;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
//传感器精度的变化
}
}
这个传感器的介绍就这些,大家应该都看得懂。赶紧拿出自己的手机来试试吧。
访问和使用传感器的总结:
①注销传感器监听器
当你完成使用传感器的事情或者当传感器activity pause的时候,确保注销传感器监听器。如果一个传感器的监听器被注册并且它的activity被pause,这个传感器将继续获取数据并且使用电池资源,除非你注销这个传感器。
mSensorManager.unregisterListener(this);
②不要在模拟器上测试你的代码
你目前不能再模拟器上测试你的传感器代码,因为模拟器不能模拟传感器。你必须在一个物理设备上测试你的传感器代码。
③不要阻塞onSensorChanged()方法
传感器数据可以高速的变化,这意味着系统可能经常调用onSensorChanged(SensorEvent)方法。作为一项最佳的实践,你应该竟可能少 的在onSensorChanged(SensorEvent)方法中做事情,所以你没有阻塞它。如果你的应用程序要求你做任何数据过滤或者减少传感器数 据,你应该在onSensorChanged(SensorEvent)方法外执行这个工作。
④避免使用过时的方法或者传感器类型
几个方法和常量已经被弃用。尤其,TYPE_ORIENTATION传感器类型已经被弃用。为了获取方向数据你应该使用getOrientation()方 法替代。同样,TYPE_TEMPERATURE传感器类型已经被弃用。你应该在运行Andorid4.0的设备上使用TYPE_AMBIENT_TEMPERATURE传感器类型替代。
⑤在你使用它们之前验证传感器
在你尝试从它获取数据之前,总是验证在一个传感器在设备上是否存在。不要因为它是一个常用的传感器而简单假设传感器存在。设备厂商没有被要求在它们的设备上提供任何指定的传感器。
⑥仔细选择传感器延迟
当你使用registerListener()方法中注册一个传感器的时候,确保你选择一个适合你的应用程序或者用例的分发率。传感器能非常高速提供数据。允许系统发送额外的你不需要浪费系统资源并使用电池的数据。