感應器總覽

大多數 Android 裝置都內建感應器,可測量動作、方向和各種環境條件。這些感應器可以提供高精確度的原始資料,當您想要監控 3D 裝置的移動或位置,或想要監控裝置附近環境環境的變化,則相當實用。舉例來說,遊戲可能會追蹤裝置重力感應器的讀數,藉此推斷複雜的使用者手勢和動作,例如傾斜、搖動、旋轉或旋轉。同樣地,天氣應用程式則可使用裝置的溫度感應器和濕度感應器計算並回報露點,或者旅遊應用程式則可使用地磁場感應器和加速計來回報指南針軸。

Android 平台支援三種多種感應器:

  • 動作感應器

    這些感應器可沿著三個軸測量加速度和旋轉力,這個類別包括加速計、重力感應器、陀螺儀和旋轉向量感應器。

  • 環境感應器

    這些感應器會測量環境氣溫、壓力、照明和濕度等各種環境參數。這個類別包含氣壓計、光量表和溫度計。

  • 位置感應器

    這些感應器可測量裝置的實際位置,這個類別包括螢幕方向感應器和磁力儀。

您可以使用 Android 感應器架構存取裝置上的感應器,並取得原始感應器資料。感應器架構提供數種類別和介面,可協助您執行各種與感應器相關的工作。舉例來說,您可以使用感應器架構執行以下作業:

  • 判斷裝置有哪些可用的感應器。
  • 決定個別感應器的功能,例如最大範圍、製造商、電源需求和解析度。
  • 取得原始感應器資料,並定義獲取感應器資料的最低速率。
  • 註冊及取消註冊用於監控感應器變化的感應器事件監聽器。

本主題概略介紹 Android 平台可用的感應器。此外,也會介紹感應器架構。

感應器簡介

Android 感應器架構可讓您存取多種感應器。其中有些感應器是以硬體為基礎,有些則是以軟體為基礎。硬體感應器是手機或平板電腦裝置內建的實體元件。他們可以直接測量特定環境屬性,例如加速度、地磁場強度或角變變化,藉此取得資料。雖然軟體式感應器模仿硬體感應器,但並非實體裝置。軟體式感應器會從一或多個硬體感應器取得資料,有時也稱為虛擬感應器或合成感應器。線性加速感應器和重力感應器都是軟體式感應器的例子。表 1 摘要說明 Android 平台支援的感應器。

只有少數 Android 裝置具備每種感應器。舉例來說,大部分手機和平板電腦都配備加速計和磁力儀,但只有少數裝置配備氣壓計或溫度計。此外,每部裝置皆可具備多個特定類型的感應器。舉例來說,裝置可以有兩個重力感應器,每個感應器都有不同的範圍。

表 1.Android 平台支援的感應器類型。

感應器 類型 說明 常見的使用方式
TYPE_ACCELEROMETER 硬體 測量 m/s2 中套用至裝置,三個實體軸 (x、y 和 z) 的加速力,包括重力力。 動作偵測 (搖動、傾斜等)。
TYPE_AMBIENT_TEMPERATURE 硬體 測量環境室溫,以攝氏度 (°C) 為單位。請見下方的附註。 監控空氣溫度。
TYPE_GRAVITY 軟體或硬體 測量以 m/s2 為單位的重力,在三個實體軸 (x、y、z) 上套用至裝置。 動作偵測 (搖動、傾斜等)。
TYPE_GYROSCOPE 硬體 測量裝置在三個實體軸 (x、y 和 z) 周圍的旋轉率 (以半徑為單位)。 旋轉偵測 (轉動、轉動等)。
TYPE_LIGHT 硬體 測量以 lx 為單位的環境光度 (照明度)。 控制螢幕亮度。
TYPE_LINEAR_ACCELERATION 軟體或硬體 測量 m/s2 中套用至裝置的加速力,範圍涵蓋所有三個實體軸 (x、y 和 z),但不包括重力。 監控單一軸的加速度。
TYPE_MAGNETIC_FIELD 硬體 測量全部三個實體軸 (x、y、z) 的環境磁場,以 μT 為單位。 建立指南針。
TYPE_ORIENTATION 軟體 測量裝置在三個實體軸 (x、y、z) 周圍的旋轉度。自 API 級別 3 起,您可以搭配使用重力感應器和幾何地表感應器,搭配 getRotationMatrix() 方法,取得裝置的傾斜矩陣和旋轉矩陣。 正在判斷裝置位置。
TYPE_PRESSURE 硬體 測量 hPa 或 mbar 環境氣壓。 監測氣壓變化。
TYPE_PROXIMITY 硬體 測量相對於裝置檢視畫面畫面的公分 (以公分為單位)。這個感應器通常用於判斷是否有手持耳機,以偵測使用者的耳朵。 通話期間的手機位置。
TYPE_RELATIVE_HUMIDITY 硬體 測量環境相對濕度,單位為百分比 (%)。 監控露點、絕對度和相對濕度。
TYPE_ROTATION_VECTOR 軟體或硬體 提供裝置旋轉向量的三個元素,藉此測量裝置的方向。 動作偵測和旋轉偵測。
TYPE_TEMPERATURE 硬體 以攝氏 (°C) 為單位測量裝置溫度。這項感應器的實作方式會因裝置而異,且這個感應器已由 API 級別 14 中的 TYPE_AMBIENT_TEMPERATURE 感應器取代 監測溫度。

感應器架構

您可以使用 Android 感應器架構,存取這些感應器並取得原始感應器資料。感應器架構是 android.hardware 套件的一部分,並包含下列類別和介面:

SensorManager
您可以使用這個類別建立感應器服務的執行個體。這個類別提供各種方法,用於存取及列出感應器、註冊及取消註冊感應器事件事件監聽器,以及取得螢幕方向資訊。這個類別還提供多個感應器常數,用於回報感應器準確率、設定資料擷取率,以及校正感應器。
Sensor
您可以使用這個類別建立特定感應器的執行個體。這個類別提供多種方法,讓您判斷感應器的功能。
SensorEvent
系統會使用這個類別建立感應器事件物件,提供感應器事件的相關資訊。感應器事件物件包含以下資訊:原始感應器資料、產生事件的感應器類型、資料的準確度,以及事件的時間戳記。
SensorEventListener
您可以利用此介面建立兩種回呼方法,以便在感應器值變更或感應器準確度變更時接收通知 (感應器事件)。

在一般應用程式中,您會使用這些感應器相關 API 來執行以下兩項基本工作:

  • 識別感應器和感應器功能

    如果應用程式的功能需要依賴特定感應器類型或功能,在執行階段找出感應器和感應器功能會很實用。舉例來說,您可以找出裝置上所有感應器,並停用需要用到感應器的任何應用程式功能。同樣地,建議您找出特定類型的所有感應器,以便選擇能為應用程式帶來最佳效能的感應器實作。

  • 監控感應器事件

    監控感應器事件是您取得原始感應器資料的方式。每次感應器偵測到測量的參數變更時,就會發生感應器事件。感應器事件提供四項資訊:觸發事件的感應器名稱、事件的時間戳記、事件準確度,以及觸發該事件的原始感應器資料。

可使用感應器

雖然感應器可用性因裝置而異,但可能會因 Android 版本而異。這是因為我們在多個平台版本過程中導入了 Android 感應器。舉例來說,許多感應器已在 Android 1.5 (API 級別 3) 中導入,但部分感應器尚未實作,在 Android 2.3 (API 級別 9) 之前無法使用。同樣地,Android 2.3 (API 級別 9) 和 Android 4.0 (API 級別 14) 也導入了多個感應器。兩個感應器已淘汰,並由較新、品質更佳的感應器取代。

表 2 摘要列出各平台每種感應器的可用性。這裡只列出四個平台,因為是涉及感應器變更的平台。列為已淘汰的感應器仍可在後續平台上 (前提是裝置上有感應器),而此感應器符合 Android 的前瞻相容性政策。

表 2. 各平台適用感應器的可用性。

感應器 Android 4.0
(API 級別 14)
Android 2.3
(API 級別 9)
Android 2.2
(API 級別 8)
Android 1.5
(API 級別 3)
TYPE_ACCELEROMETER
TYPE_AMBIENT_TEMPERATURE n/a n/a n/a
TYPE_GRAVITY n/a n/a
TYPE_GYROSCOPE 不適用1 不適用1
TYPE_LIGHT
TYPE_LINEAR_ACCELERATION n/a n/a
TYPE_MAGNETIC_FIELD
TYPE_ORIENTATION 2 2 2
TYPE_PRESSURE 不適用1 不適用1
TYPE_PROXIMITY
TYPE_RELATIVE_HUMIDITY n/a n/a n/a
TYPE_ROTATION_VECTOR n/a n/a
TYPE_TEMPERATURE 2

1 這個感應器類型已在 Android 1.5 (API 級別 3) 中新增,但在 Android 2.3 (API 級別 9) 之前無法使用。

2 這個感應器可供使用,但已淘汰。

識別感應器和感應器功能

Android 感應器架構提供多種方法,方便您在執行階段輕鬆判斷裝置上的感應器。這個 API 也提供多種方法,讓您決定每個感應器的功能,例如最大範圍、解析度和功率需求。

為了辨識裝置上的感應器,您必須先取得感應器服務的參照。做法是呼叫 getSystemService() 方法並傳入 SENSOR_SERVICE 引數,藉此建立 SensorManager 類別的執行個體。例如:

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

Java

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

接下來,您可以呼叫 getSensorList() 方法並使用 TYPE_ALL 常數,取得裝置上每個感應器的清單。例如:

Kotlin

val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)

Java

List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);

如要列出特定類型的所有感應器,您可以使用其他常數,而非 TYPE_ALL,例如 TYPE_GYROSCOPETYPE_LINEAR_ACCELERATIONTYPE_GRAVITY

您也可以使用 getDefaultSensor() 方法並傳入特定感應器的類型常數,藉此判斷裝置上是否有特定類型的感應器。如果裝置有多個指定類型的感應器,則必須將其中一個感應器指定為預設感應器。如果指定的感應器類型不存在預設感應器,方法呼叫會傳回空值,這表示裝置沒有該感應器類型。舉例來說,以下程式碼會檢查裝置上是否有磁力儀:

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null) {
    // Success! There's a magnetometer.
} else {
    // Failure! No magnetometer.
}

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){
    // Success! There's a magnetometer.
} else {
    // Failure! No magnetometer.
}

注意:Android 並未要求裝置製造商在 Android 裝置上建構任何特定類型的感應器,因此裝置可以採用多種感應器設定。

除了列出裝置上的感應器外,您也可以使用 Sensor 類別的公開方法判斷個別感應器的功能和屬性。如果您希望應用程式根據裝置可使用的感應器或感應器功能做出不同行為,這個方法就能派上用場。舉例來說,您可以使用 getResolution()getMaximumRange() 方法取得感應器的解析度和最大測量範圍。您也可以使用 getPower() 方法取得感應器的電源需求。

如果想要針對不同製造商的感應器或不同版本的感應器最佳化您的應用程式,以下兩個公開方法特別實用。舉例來說,如果應用程式需要監控傾斜和搖晃等使用者手勢,您可以針對具有特定廠商重力感應器的新型裝置建立一組資料篩選規則和最佳化,並為沒有重力感應器且只有加速計的裝置建立一組資料篩選規則和最佳化。以下程式碼範例說明如何使用 getVendor()getVersion() 方法執行此操作。在本範例中,我們正在尋找重力感應器,其中將 Google LLC 列為供應商,且版本編號為 3。如果裝置上沒有這個特定感應器,我們會嘗試使用加速計。

Kotlin

private lateinit var sensorManager: SensorManager
private var mSensor: Sensor? = null

...

sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

if (sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null) {
    val gravSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_GRAVITY)
    // Use the version 3 gravity sensor.
    mSensor = gravSensors.firstOrNull { it.vendor.contains("Google LLC") && it.version == 3 }
}
if (mSensor == null) {
    // Use the accelerometer.
    mSensor = if (sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
    } else {
        // Sorry, there are no accelerometers on your device.
        // You can't play this game.
        null
    }
}

Java

private SensorManager sensorManager;
private Sensor mSensor;

...

sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = null;

if (sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null){
    List<Sensor> gravSensors = sensorManager.getSensorList(Sensor.TYPE_GRAVITY);
    for(int i=0; i<gravSensors.size(); i++) {
        if ((gravSensors.get(i).getVendor().contains("Google LLC")) &&
           (gravSensors.get(i).getVersion() == 3)){
            // Use the version 3 gravity sensor.
            mSensor = gravSensors.get(i);
        }
    }
}
if (mSensor == null){
    // Use the accelerometer.
    if (sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){
        mSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    } else{
        // Sorry, there are no accelerometers on your device.
        // You can't play this game.
    }
}

另一個實用方法是 getMinDelay() 方法,此方法會傳回感應器可用來感應資料的最短時間間隔 (以毫秒為單位)。凡是為 getMinDelay() 方法傳回非零值的感應器,都是串流感應器。串流感應器會定期偵測資料,並在 Android 2.3 (API 級別 9) 中推出。如果感應器在您呼叫 getMinDelay() 方法時傳回零,代表感應器不是串流感應器,因為只有在偵測到的參數有所變更時,才會回報資料。

getMinDelay() 方法非常實用,因為可讓您判斷感應器取得資料的頻率上限。如果應用程式中的某些功能需要高資料擷取率或串流感應器,您可以使用此方法判斷感應器是否符合這些需求,據此啟用或停用應用程式中的相關功能。

注意:感應器的資料擷取率上限不一定是感應器架構將感應器資料傳送至應用程式的頻率。感應器架構會透過感應器事件回報資料,而有幾項因素會影響應用程式接收感應器事件的頻率。詳情請參閱「監控感應器事件」一文。

監控感應器事件

如要監控原始感應器資料,您必須實作透過 SensorEventListener 介面公開的兩種回呼方法:onAccuracyChanged()onSensorChanged()。發生下列情況時,Android 系統會呼叫這些方法:

以下程式碼說明如何使用 onSensorChanged() 方法監控光度感應器提供的資料。這個範例會在 main.xml 檔案內定義為 sensor_dataTextView 中顯示原始感應器資料。

Kotlin

class SensorActivity : Activity(), SensorEventListener {
    private lateinit var sensorManager: SensorManager
    private var mLight: Sensor? = null

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        mLight = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
    }

    override fun onSensorChanged(event: SensorEvent) {
        // The light sensor returns a single value.
        // Many sensors return 3 values, one for each axis.
        val lux = event.values[0]
        // Do something with this sensor value.
    }

    override fun onResume() {
        super.onResume()
        mLight?.also { light ->
            sensorManager.registerListener(this, light, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    override fun onPause() {
        super.onPause()
        sensorManager.unregisterListener(this)
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor mLight;

    @Override
    public final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mLight = sensorManager.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();
        sensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL);
    }

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

在這個範例中,系統會在叫用 registerListener() 方法時指定預設的資料延遲時間 (SENSOR_DELAY_NORMAL)。資料延遲 (或取樣率) 控制了透過 onSensorChanged() 回呼方法將感應器事件傳送至應用程式的時間間隔。預設資料延遲適用於監控一般螢幕方向變更,且延遲時間為 200,000 微秒。您可以指定其他資料延遲,例如 SENSOR_DELAY_GAME (延遲 20,000 微秒)、SENSOR_DELAY_UI (60,000 微秒延遲) 或 SENSOR_DELAY_FASTEST (延遲時間為 0 微秒)。自 Android 3.0 (API 級別 11) 起,您也可以將延遲時間指定為絕對值 (以微秒為單位)。

您指定的延遲時間只是建議的延遲時間。Android 系統和其他應用程式可能會改變此延遲時間。最佳做法是,系統通常會使用比您指定的最短延遲,因為系統通常會使用比您指定的最短延遲,因為您應選擇仍能滿足應用程式需求的最低取樣率。使用較長的延遲時間會讓處理器的負載較低,進而減少耗電量。

目前還沒有公開方法讓您判斷感應器架構傳送感應器事件至應用程式的速率。不過,您可以使用與各個感應器事件相關聯的時間戳記,計算多個事件的取樣率。設定取樣率 (延遲) 後,你就不需要變更。如果您需要變更延遲時間,就必須取消註冊並重新註冊感應器事件監聽器。

請注意,本範例使用 onResume()onPause() 回呼方法來註冊及取消註冊感應器事件事件監聽器。最佳做法是一律停用不需要的感應器,特別是在活動暫停時。否則可能會因為部分感應器的電量需求而快速消耗電池電力,因此短短幾小時內就能耗盡電力。系統不會在螢幕關閉時自動停用感應器。

處理不同的感應器設定

Android 不會為裝置指定標準感應器設定,這表示裝置製造商可將自己所需的任何感應器設定加入 Android 裝置中。因此,裝置可以提供多種不同的感應器設定。如果應用程式仰賴特定類型的感應器,您必須確保裝置上有感應器,應用程式才能順利執行。

如要確保裝置會顯示指定的感應器,您有以下兩種方法:

  • 在執行階段偵測感應器,並視情況啟用或停用應用程式功能。
  • 使用 Google Play 篩選器指定具有特定感應器設定的裝置。

我們將在以下章節中討論每個選項。

在執行階段偵測感應器

如果應用程式使用特定類型的感應器,但未仰賴這項功能,您可以使用感應器架構偵測在執行階段中的感應器,然後視情況停用或啟用應用程式功能。舉例來說,導航應用程式可能會使用溫度感應器、壓力感應器、GPS 感應器和地磁場感應器,顯示溫度、氣壓、位置和指南針方位。如果裝置沒有壓力感應器,您可以使用感應器架構偵測在執行階段是否沒有壓力感應器,然後停用應用程式使用者介面 (此部分會顯示壓力) 的部分。舉例來說,以下程式碼會檢查裝置上是否有壓力感應器:

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null) {
    // Success! There's a pressure sensor.
} else {
    // Failure! No pressure sensor.
}

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null){
    // Success! There's a pressure sensor.
} else {
    // Failure! No pressure sensor.
}

使用 Google Play 篩選器指定特定的感應器設定

如果您是在 Google Play 發布應用程式,可以使用資訊清單檔案中的 <uses-feature> 元素,篩選出未為應用程式採用適當感應器設定的裝置。<uses-feature> 元素有多個硬體描述元,可讓您根據是否有特定感應器篩選應用程式。可以加入的感應器包括:加速計、氣壓計、指南針 (地理磁場)、陀螺儀、光度和鄰近區域。以下範例資訊清單項目可篩選沒有加速計的應用程式:

<uses-feature android:name="android.hardware.sensor.accelerometer"
              android:required="true" />

如果您在應用程式資訊清單中加入這個元素和描述元,則使用者的裝置必須配備加速計,才能在 Google Play 上看到您的應用程式。

只有在應用程式完全位於特定感應器上時,您才應將描述元設為 android:required="true"。如果應用程式使用感應器提供部分功能,但仍可在沒有感應器的情況下執行,建議您在 <uses-feature> 元素中列出感應器,但將描述元設為 android:required="false"。如此一來,即使裝置沒有該感應器,仍然可以安裝應用程式。這也是專案管理最佳做法,可協助您追蹤應用程式使用的功能。請注意,如果應用程式使用特定感應器,但仍在不使用感應器的情況下執行,則應在執行階段偵測感應器,並視情況停用或啟用應用程式功能。

感應器座標系統

一般而言,感應器架構使用標準的 3 軸座標系統表示資料值。對大多數感應器而言,在裝置以預設螢幕方向保持狀態時,座標系統是相對於裝置螢幕的定義 (請參閱圖 1)。當裝置以預設螢幕方向保持開啟時,X 軸是水平方向並指向右側,Y 軸代表垂直方向,Z 軸則指向錶面外側。在這個系統中,螢幕後方的座標會有負的 Z 值。下列感應器正在使用此座標系統:

圖 1. 感應器 API 使用的座標系統 (相對於裝置)。

瞭解這個座標系統最重要的一點,是當裝置的螢幕方向變更時,軸不會旋轉,也就是感應器的座標系統絕不會隨著裝置移動而改變。此行為與 OpenGL 座標系統的行為相同。

另一個要瞭解的重點是,應用程式不得假設裝置的自然 (預設) 螢幕方向為直向。許多平板電腦裝置的自然螢幕方向都是橫向。感應器座標系統一律會以裝置的自然方向為依據。

最後,如果應用程式比對感應器資料與螢幕顯示內容,您必須使用 getRotation() 方法決定螢幕旋轉角度,然後使用 remapCoordinateSystem() 方法將感應器座標對應至螢幕座標。即使資訊清單指定了僅限直向顯示,您仍須執行這項操作。

注意:部分感應器和方法使用的座標系統與世界參考框架相關 (相對於裝置參考框架)。這些感應器和方法會傳回的資料,代表相對於地球的裝置動作或裝置位置。詳情請參閱 getOrientation() 方法、getRotationMatrix() 方法、螢幕方向感應器以及旋轉向量感應器

感應器頻率限制

為了保護使用者的潛在機密資訊,如果應用程式指定 Android 12 (API 級別 31) 以上版本,系統會限制來自特定動作感應器和位置感應器的資料重新整理頻率。這項資料包含裝置加速計陀螺儀地磁場感應器記錄的值。

重新整理頻率限制取決於您存取感應器資料的方式:

如果應用程式需要以更高的頻率收集動作感應器資料,您必須宣告 HIGH_SAMPLING_RATE_SENSORS 權限,如以下程式碼片段所示。否則,如果應用程式嘗試以更高的頻率收集動作感應器資料,卻未宣告這項權限,就會發生 SecurityException

AndroidManifest.xml

<manifest ...>
    <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/>
    <application ...>
        ...
    </application>
</manifest>

存取及使用感應器的最佳做法

設計感應器實作時,請務必遵循本節討論的指南。如果有人使用感應器架構存取感應器和取得感應器資料,以下是建議的最佳做法。

僅收集前景的感應器資料

在搭載 Android 9 (API 級別 28) 以上版本的裝置上,在背景執行的應用程式設有以下限制:

  • 使用連續回報模式的感應器 (例如加速計和陀螺儀) 無法接收事件。
  • 使用零變更一次性回報模式的感應器不會接收事件。

由於上述限制,最好在應用程式於前景執行或前景服務時偵測感應器事件。

取消註冊感應器監聽器

使用感應器或感應器活動暫停時,請務必取消註冊感應器的事件監聽器。如果感應器監聽器已註冊且活動暫停,除非取消註冊感應器,否則感應器將繼續取得資料並使用電池資源。以下程式碼說明如何使用 onPause() 方法取消註冊事件監聽器:

Kotlin

private lateinit var sensorManager: SensorManager
...
override fun onPause() {
    super.onPause()
    sensorManager.unregisterListener(this)
}

Java

private SensorManager sensorManager;
...
@Override
protected void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
}

詳情請參閱 unregisterListener(SensorEventListener)

使用 Android Emulator 進行測試

Android Emulator 提供一組虛擬感應器控制項,可讓您測試加速計、環境溫度、磁力儀、鄰近、光度等感應器。

模擬器使用連線執行 SdkControllerSensor 應用程式的 Android 裝置。請注意,這個應用程式僅適用於搭載 Android 4.0 (API 級別 14) 以上版本的裝置。(如果裝置執行的是 Android 4.0,則必須安裝修訂版本 2)。SdkControllerSensor 應用程式會監控裝置上的感應器變更,並將這些變更傳送至模擬器。接著,模擬器會根據裝置感應器接收的新值進行轉換。

您可以在下列位置查看 SdkControllerSensor 應用程式的原始碼:

$ your-android-sdk-directory/tools/apps/SdkController

如要在裝置和模擬器之間傳輸資料,請按照下列步驟操作:

  1. 確認裝置已啟用 USB 偵錯功能
  2. 使用 USB 傳輸線將裝置連接至開發機器。
  3. 在裝置上啟動 SdkControllerSensor 應用程式。
  4. 在應用程式中選取要模擬的感應器。
  5. 執行下列 adb 指令:

  6. $ adb forward tcp:1968 tcp:1968
    
  7. 啟動模擬器。現在只要移動裝置,就能為模擬器套用轉換。

注意: 如果您對實體裝置的動作無法轉換模擬器,請嘗試再次執行步驟 5 的 adb 指令。

詳情請參閱 Android Emulator 指南

不要封鎖 onSensorChanged() 方法

感應器資料變化速率可能偏高,這代表系統可能會頻繁呼叫 onSensorChanged(SensorEvent) 方法。最佳做法是在 onSensorChanged(SensorEvent) 方法中盡可能減少執行,以免封鎖該方法。如果應用程式要求您執行任何資料篩選或縮減感應器資料,則應在 onSensorChanged(SensorEvent) 方法以外的位置執行該作業。

避免使用已淘汰的方法或感應器類型

淘汰多種方法和常數。具體而言,TYPE_ORIENTATION 感應器類型已淘汰。如要取得方向資料,請改用 getOrientation() 方法。同樣地,TYPE_TEMPERATURE 感應器類型已淘汰。請在搭載 Android 4.0 的裝置上改用 TYPE_AMBIENT_TEMPERATURE 感應器類型。

使用感應器前請先驗證

在嘗試從裝置上取得資料前,請務必確認裝置上有感應器。請勿假設感應器確實存在,因為這是常用的感應器。裝置製造商不需要在自家裝置上提供任何特定感應器。

仔細選擇感應器的延遲狀況

使用 registerListener() 方法註冊感應器時,請務必選擇適合您的應用程式或用途的傳送率。感應器能以極高的速度提供資料。允許系統傳送不需要的額外資料,以免浪費系統資源並消耗電池電力。