Android開發高級進階——傳感器

Android系統提供了對傳感器的支持,如果手機設備的硬件提供了這些傳感器,Android應用可以通過傳感器來獲取設備的外界條件,包括手機設備的運行狀態、當前擺放方向、外界的磁場、溫度和壓力等。Android系統提供了驅動程序去管理這些傳感器硬件,當傳感器感知到外部環境發生改變時,Android系統負責管理這些傳感器數據。

一. Android中11中常見的傳感器


  1. 加速度傳感器:SENSOR_TYPE_ACCELEROMETER
  • 磁力傳感器:SENSOR_TYPE_FIELD
  • 方向傳感器:SENSOR_TYPE_ORIENTATION
  • 陀螺儀傳感器:SENSOR_TYPE_GYROSCOPE
  • 光線感應傳感器:SENSOR_TYPE_LIGHT
  • 壓力傳感器:SENSOR_TYPE_PRESSURE
  • 溫度傳感器:SENSOR_TYPE_TEMPERATURE
  • 接近傳感器:SENSOR_TYPE_PROXIMITY
  • 重力傳感器:SENSOR_TYPE_GRAVITY
  • 線性加速度傳感器:SENSOR_TYPE_LINEAR_ACCELERATION
  • 旋轉矢量傳感器:SENSOR_TYPE_ROTATION_VECTOR

二. 使用傳感器


使用傳感器的步驟分為5步:

  1. 獲取SensorManager對象
    調用Context的getSystemService(Context.SENSOR_SERVICE)方法獲取SensorManager對象,SensorManager對象代表系統的傳感器管理服務。
  2. 獲取Sensor對象
    調用SensorManager的getDefaultSensor(int type)方法獲取指定類型的傳感器。
  3. 注冊Sensor對象
    在Activity的onResume()方法中調用SensorManager的registerListener()方法為指定的傳感器注冊監聽器,程序通過實現監聽器即可獲取傳感器傳來的數據。
  4. 重寫onAccuracyChanged,onSensorChanged方法
    當傳感器的精度和數據發送變化時,在這兩個方法中做相應的操作。
  5. 注銷Sensor對象
    在Activity的onPause()方法中調用SensorManager的unregisterListener()方法注銷指定的傳感器監聽器。

SensorManager提供的注冊傳感器的方法為registerListener(SensorEventListener listener, Sensor sensor, int rate),該方法的三個參數說明如下:

  • listener:監聽傳感器事件的監聽器。該監聽器需要實現SensorEventListener接口。
  • sensor:傳感器對象。
  • rate:指定獲取傳感器數據的頻率。rate有以下幾個頻率值:
    • SensorManager.SENSOR_DELAY_FASTEST:最快。延遲最小,只有特別依賴于傳感器數據的應用推薦采用這種頻率,這種模式可能造成手機電量大量消耗。
    • SensorManager.SENSOR_DELAY_GAME:適合游戲的頻率。一般有實時性要求的應用適合使用這種頻率。
    • SensorManager.SENSOR_DELAY_NORMAL:正常頻率。一般對實時性要求不是特別高的應用適合使用這種頻率。
    • SensorManager.SENSOR_DELAY_UI:適合普通用戶界面的頻率。這種模式比較省電,而且系統開銷也很小,但延遲較大。

三. 讀取傳感器數據


在onSensorChanged(SensorEvent event)方法中有一個參數event,通過event可以獲取傳感器的類型以及傳感器的數據。

  • 獲取傳感器的類型:event.sensor.getType()
  • 獲取傳感器的數據:event.values[i],i為0,1,2...,不同傳感器,event.values[i]對應的數據不同,下面以加速度傳感器為例,解釋values[i]的含義。
* <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER
     * Sensor.TYPE_ACCELEROMETER}:</h4> All values are in SI units (m/s^2)
     * <ul>
     * <li> values[0]: Acceleration minus Gx on the x-axis </li>
     * <li> values[1]: Acceleration minus Gy on the y-axis </li>
     * <li> values[2]: Acceleration minus Gz on the z-axis </li>
     * </ul>
     * <p>
     * A sensor of this type measures the acceleration applied to the device
     * (<b>Ad</b>). Conceptually, it does so by measuring forces applied to the
     * sensor itself (<b>Fs</b>) using the relation:
     * </p>
     * <b><center>Ad = - &#8721;Fs / mass</center></b>
     * <p>
     * In particular, the force of gravity is always influencing the measured
     * acceleration:
     * </p>
     * <b><center>Ad = -g - &#8721;F / mass</center></b>
     * <p>
     * For this reason, when the device is sitting on a table (and obviously not
     * accelerating), the accelerometer reads a magnitude of <b>g</b> = 9.81
     * m/s^2
     * </p>
     * <p>
     * Similarly, when the device is in free-fall and therefore dangerously
     * accelerating towards to ground at 9.81 m/s^2, its accelerometer reads a
     * magnitude of 0 m/s^2.
     * </p>
     * <p>
     * It should be apparent that in order to measure the real acceleration of
     * the device, the contribution of the force of gravity must be eliminated.
     * This can be achieved by applying a <i>high-pass</i> filter. Conversely, a
     * <i>low-pass</i> filter can be used to isolate the force of gravity.
     * </p>
     * <pre class="prettyprint">
     *     public void onSensorChanged(SensorEvent event)
     *     {
     *          // alpha is calculated as t / (t + dT)
     *          // with t, the low-pass filter's time-constant
     *          // and dT, the event delivery rate
     *          final float alpha = 0.8;
     *          gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
     *          gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
     *          gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
     *          linear_acceleration[0] = event.values[0] - gravity[0];
     *          linear_acceleration[1] = event.values[1] - gravity[1];
     *          linear_acceleration[2] = event.values[2] - gravity[2];
     *     }
     * </pre>
     * <p>
     * <u>Examples</u>:
     * <ul>
     * <li>When the device lies flat on a table and is pushed on its left side
     * toward the right, the x acceleration value is positive.</li>
     * <li>When the device lies flat on a table, the acceleration value is
     * +9.81, which correspond to the acceleration of the device (0 m/s^2) minus
     * the force of gravity (-9.81 m/s^2).</li>
     * <li>When the device lies flat on a table and is pushed toward the sky
     * with an acceleration of A m/s^2, the acceleration value is equal to
     * A+9.81 which correspond to the acceleration of the device (+A m/s^2)
     * minus the force of gravity (-9.81 m/s^2).</li>
     * </ul>

從加速度傳感器源代碼中可以看出,values[0]表示x軸上的加速度,values[1]表示y軸上的加速度,values[2]表示z軸上的加速度。

四. 針對是否有傳感器功能優化


因為并非所有手機都支持所有傳感器,不用系統引入的傳感器不同,所以在使用之前有必要判斷一下,、從而提高性能。

判斷是否有傳感器有兩種方法:

  1. 運行時檢測
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
if (sensor != null){
          //傳感器存在
}else{
          //傳感器不存在
}
  1. 使用Android Market過濾器來限定目標設備必須帶有指定傳感器配置。
<use-feature 
       name = "android.hardware.sensor.orientation"
       android:required = "true"/>

五. 方向傳感器小Demo


利用方向傳感器,界面中的圖片向手機旋轉的反方向旋轉。代碼如下:

public class MainActivity extends AppCompatActivity implements SensorEventListener{

    private ImageView mIvSensor;
    private Sensor mSensor;
    private SensorManager mSensorManager;
    private float mDegress = 0f;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mIvSensor = (ImageView) findViewById(R.id.iv_sensor);

        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);

    }

    @Override
    protected void onResume() {
        super.onResume();
        mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_UI); //rate suitable for the user interface
    }

    @Override
    protected void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(this);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ORIENTATION){
            float degree = - event.values[0];
            RotateAnimation rotateAnimation = new RotateAnimation(mDegress, degree, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            rotateAnimation.setDuration(100);
            mIvSensor.startAnimation(rotateAnimation);
            mDegress = degree;
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        //TODO:當傳感器精度發生變化時
    }
}

演示效果:

orientation_sensor_demo.gif

六. 注意


  1. 別忘記注銷。
  2. 不要阻塞onSensorChanged方法。
  3. 避免使用過時的方法或傳感器類型。
  4. 在使用前先驗證傳感器是否存在。
  5. 謹慎選擇傳感器延時。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android傳感器定義 Android 傳感器相關術語微機電傳感器(MEMS)MEMS 通常制作在規格很小的硅芯...
    Jannonx閱讀 4,418評論 0 1
  • 傳感器 傳感器Sensor是一種檢測裝置,能感受到被測量的信息,并能將感受到的信息,按一定規律變換成為電信號或其他...
    Reathin閱讀 6,418評論 0 5
  • 先說一下這篇文章里面的內容:TCP 客戶端, 自定義對話框, 自定義按鈕, ProgressBar豎直顯示, 重力...
    楊奉武閱讀 3,373評論 0 3
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 短似詞語的創作,今天并不稀奇,或許有些粗鄙,有些無聊,甚至到了廣告詞一樣“瘟疫”的目的。好的方面,詞語的或音節的“...
    卜生閱讀 706評論 0 3