提到手機上的傳感器大家應該都不陌生,比如說方向傳感器、陀螺儀、重力傳感器、光線傳感器等,現在的手機基本集成少說有十幾種傳感器,面向將來的人工智能,這塊知識也是我們必備的,
傳感器分類
Android 平臺支持三大類傳感器:
- 動態傳感器
這類傳感器測量三個軸向上的加速力和旋轉力,包含加速度傳感器、重力傳感器、陀螺儀和旋轉矢量傳感器, - 環境傳感器
這類傳感器測量各種環境引數,如環境氣溫、氣壓、照度和濕度,這個類別中包含氣壓計、光度計和溫度計, - 位置傳感器
這類傳感器測量設備的物理位置,這個類別中包含螢屏方向傳感器和磁力計(電子羅盤),
傳感器使用
Android平臺上google已經為我們提供了一套很方便的傳感器框架,我們只需在這個框架下定義自己要用的傳感器并實作其邏輯即可,
傳感器框架是 android.hardware 軟體包的一部分,開發使用程序中主要用到了以下類和介面:
- SensorManager
- Sensor
- SensorEventListener
- SensorEvent
第一步:獲取傳感器管理物件
SensorManager mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
第二步:指定傳感器目標型別
// 獲取傳感器的型別(TYPE_ACCELEROMETER:加速度傳感器)
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
這里指定的傳感器型別有很多種,這里暫且列舉一些最常用的傳感器:
- Sensor.TYPE_ACCELEROMETER: 三軸加速度傳感器
- Sensor.TYPE_ORIENTATION:方向傳感器
- Sensor.TYPE_GYROSCOPE:陀螺儀傳感器
- Sensor.TYPE_MAGNETIC_FIELD:磁力傳感器
- Sensor.TYPE_GRAVITY:重力傳感器
- Sensor.TYPE_LINEAR_ACCELERATION:線性加速度傳感器
- Sensor.TYPE_AMBIENT_TEMPERATURE:溫度傳感器
- Sensor.TYPE_LIGHT:光傳感器
- Sensor.TYPE_PRESSURE:壓力傳感器
- Sensor.TYPE_ALL:所有型別的傳感器,通過
getSensorList回傳所有支持的傳感器串列,
第三步:注冊傳感器監聽
SensorEventListener mListener = new SensorEventListener() {
// 當傳感器的值改變的時候回呼該方法
@Override
public void onSensorChanged(SensorEvent event) {
// Do something with this sensor value.
// The light sensor returns a single value.
// Many sensors return 3 values, one for each axis.
}
// 當傳感器精度發生改變時回呼該方法
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do something here if sensor accuracy changes.
}
}
mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_GAME);
其中registerListener最后一個引數是指定傳感器獲取資料的頻率,有以下四種形式:
- SensorManager.SENSOR_DELAY_NORMAL,正常頻率,一般對實時性要求不高的應用適合使用這種頻率,
- SensorManager.SENSOR_DELAY_FASTEST,最快,延遲最小,同時也最消耗資源,一般只有特別依賴傳感器的應用使用該頻率,否則不推薦,
- SensorManager.SENSOR_DELAY_GAME,適合游戲的頻率,一般有實時性要求的應用適合使用這種頻率,
- SensorManager.SENSOR_DELAY_UI,適合普通應用的頻率,這種模式比較省電,而且系統開銷小,但延遲大,因此只適合普通小程式使用,
一旦傳感器監測到環境資料變化,便會回呼兩個實作方法:
-
onSensorChanged(SensorEvent event):傳感器報告了新值,
內部的SensorEvent 物件包含關于新傳感器資料的資訊,包括:資料的準確度、生成資料的傳感器、生成資料的時間戳以及傳感器記錄的新資料,系統可能會頻繁呼叫,切記請勿阻塞 onSensorChanged() 方法, -
onAccuracyChanged(Sensor sensor, int accuracy):傳感器的準確度發生了變化,
當傳感器精度發生變化時onAccuracyChanged被觸發,第一引數是發生變化的sensor物件,第二個引數是傳感器的新精度(準確度),精度有以下四種:- SENSOR_STATUS_ACCURACY_LOW:低精度值
- SENSOR_STATUS_ACCURACY_MEDIUM:平均精度值
- SENSOR_STATUS_ACCURACY_HIGH:高精度值
- SENSOR_STATUS_UNRELIABLE:精度值不可靠
第四步:解注冊傳感器監聽
mSensorManager.unregisterListener(mListener)
傳感器幾乎是非常消耗資源的,最佳的做法是始終停用不需要的傳感器,特別是在活動處于暫停狀態時,如果不這樣做,可能會在幾小時內將電池電量耗盡,
第三步和第四步開發程序中一般是相呼應的,我們常常在onResume中進行注冊,在onPause中進行解注冊,也可根據業務需要自行確定時機,
運行時檢測傳感器
有時我們不能確定當前設備是否包含我們所需的傳感器,所以在運行時我們需要做一個判斷:
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
if(sensor != null){
// Success! There's a pressure sensor.
}else{
// Failure! No pressure sensor.
}
如果您要在 Google Play 上發布應用,您可以在清單檔案中使用 <uses-feature> 元素,以對不具備適合您應用的傳感器配置的設備屏蔽您的應用,以下清單示例條目會對沒有加速度計的設備屏蔽應用:
<uses-feature android:name="android.hardware.sensor.accelerometer"
android:required="true" />
傳感器坐標系
大部分傳感器都是以三軸坐標來表示資料值,對于大多數傳感器,當設備處于默認螢屏方向(平板和手機就有可能不一樣)時,會相對于設備螢屏來定義坐標系,如下圖:

以下傳感器使用此坐標系:
- 加速度傳感器
- 重力傳感器
- 陀螺儀
- 線性加速度傳感器
- 地磁場傳感器
記著,傳感器的坐標系是始終跟隨設備的,
小栗子
下邊就是通過三軸加速度傳感器實作一個計步器功能,代碼來源于網路,個人稍加注釋:
public class StepDetector implements SensorEventListener{
private final static String TAG = "StepDetector";
private float mLimit = 10;
private float mLastValues[] = new float[3*2];
private float mScale[] = new float[2];
private float mYOffset;
private float mLastDirections[] = new float[3*2];
private float mLastExtremes[][] = { new float[3*2], new float[3*2] };
private float mLastDiff[] = new float[3*2];
private int mLastMatch = -1;
private ArrayList<StepListener> mStepListeners = new ArrayList<StepListener>();
public StepDetector() {
// 手機螢屏的高
int h = 480;
// 中心點,y軸的偏移量
mYOffset = h * 0.5f;
// 獲取重力相關引數
mScale[0] = - (h * 0.5f * (1.0f / (SensorManager.STANDARD_GRAVITY * 2)));
mScale[1] = - (h * 0.5f * (1.0f / (SensorManager.MAGNETIC_FIELD_EARTH_MAX)));
}
public void setSensitivity(float sensitivity) {
//靈敏度 1.97 2.96 4.44 6.66 10.00 15.00 22.50 33.75 50.62
mLimit = sensitivity;
}
public void addStepListener(StepListener sl) {
mStepListeners.add(sl);
}
//public void onSensorChanged(int sensor, float[] values) {
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
synchronized (this) {
int j = (sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? 1 : 0;
if (j == 1) {
//三個方向上的速度和
float vSum = 0;
for (int i=0 ; i<3 ; i++) {
final float v = mYOffset + event.values[i] * mScale[j];
vSum += v;
}
int k = 0;
//平均速度
float v = vSum / 3;
float direction = (v > mLastValues[k] ? 1 : (v < mLastValues[k] ? -1 : 0));
if (direction == - mLastDirections[k]) {
// Direction changed
int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
mLastExtremes[extType][k] = mLastValues[k];
float diff = Math.abs(mLastExtremes[extType][k] - mLastExtremes[1 - extType][k]);
//運動太慢,忽略
if (diff > mLimit) {
boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k]*2/3);
boolean isPreviousLargeEnough = mLastDiff[k] > (diff/3);
boolean isNotContra = (mLastMatch != 1 - extType);
//判斷有效的一步
if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough && isNotContra) {
Log.i(TAG, "step");
for (StepListener stepListener : mStepListeners) {
stepListener.onStep();
}
mLastMatch = extType;
}
else {
mLastMatch = -1;
}
}
mLastDiff[k] = diff;
}
mLastDirections[k] = direction;
mLastValues[k] = v;
}
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
}
參考
- https://developer.android.com/guide/topics/sensors/sensors_overview
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/271602.html
標籤:其他
下一篇:Android 一種簡單的標題欄
