センサーの概要

ほとんどの Android 搭載デバイスには、動きや向きに加え、さまざまな環境条件を測定するためのセンサーが組み込まれています。これらのセンサーからは正確で高精度な測定データを取得できるため、デバイスの 3 次元での動きや位置、または周囲の環境変化をモニタリングする場合に有用です。たとえばゲームでは、重力センサーでの読み取り値をトラッキングすることで、ユーザーの複雑な操作や動き(ティルト、シェイク、回転、スウィングなど)を推定できます。また、天気情報アプリで温度センサーと湿度センサーを使って露点温度を計算したり、旅行アプリで地磁気センサーと加速度計を使ってコンパスの方位磁針を表示したりできます。

Android プラットフォームは、大きく分けて次の 3 つのカテゴリのセンサーをサポートしています。

  • モーション センサー

    3 軸方向の加速力や回転力を測定するためのセンサーです。このカテゴリのセンサーには、加速度計、重力センサー、ジャイロスコープ、回転ベクトル センサーなどがあります。

  • 環境センサー

    周囲の気温や気圧、照度、湿度など、さまざまな環境パラメータを測定するためのセンサーです。このカテゴリのセンサーには、気圧計、光度計、温度計などがあります。

  • 位置センサー

    デバイスの物理的な位置を測定するためのセンサーです。このカテゴリのセンサーには、方位センサーや磁力計などがあります。

Android センサー フレームワークを使用することにより、そのデバイスで利用可能なセンサーから測定データを取得できます。このセンサー フレームワークには、さまざまなセンサー関連タスクに役立つクラスとインターフェースが複数用意されています。たとえば、このセンサー フレームワークを使用して次のことを行えます。

  • そのデバイスで使用できるセンサーを特定する。
  • 個々のセンサーの性能(最大測定範囲、メーカー、所要電力、解像度など)を特定する。
  • センサーから測定データを取得する。また、測定データを取得する際の最低速度を定義する。
  • センサーの変化をモニタリングするセンサー イベント リスナーの登録や登録解除を行う。

このトピックでは、Android プラットフォームで利用できるセンサーの概要を説明します。また、Android センサー フレームワークの概要についても説明します。

センサーの概要

Android センサー フレームワークでは、さまざまなタイプのセンサーを利用できます。センサーには、ハードウェアベースのものと、ソフトウェアベースのものがあります。ハードウェアベース センサーは、モバイルやタブレットのデバイスに組み込まれた物理的なコンポーネントです。特定の環境特性(加速度、地磁気強度、角度変化など)を直接測定することでデータを導出します。ソフトウェアベース センサーは、ハードウェアベース センサーを模したもので、物理的なデバイスではありません。1 つまたは複数のハードウェアベース センサーからデータを取得するもので、仮想センサーまたは合成センサーとも呼ばれます。ソフトウェアベース センサーの例としては、リニア加速度センサーや重力センサーが挙げられます。表 1 に、Android プラットフォームでサポートされているセンサーをまとめます。

すべてのタイプのセンサーを備えた Android 搭載デバイスはまれにしかありません。たとえば、ほとんどのモバイル デバイスやタブレットは加速度計と磁力計を備えていますが、気圧計や温度計を備えたものはそれほどありません。また、1 つのデバイスに同じタイプのセンサーが複数搭載されていることもあります。たとえば、1 つのデバイスに、測定範囲の異なる 2 つの重力センサーが搭載されている場合があります。

表 1. Android プラットフォームでサポートされているセンサータイプ

センサー 種類 説明 一般的な活用法
TYPE_ACCELEROMETER ハードウェア デバイスにかかる 3 本の物理軸(x、y、z)方向の加速力(重力も含む)を m/s2 単位で測定します。 動きの検出(シェイク、ティルトなど)。
TYPE_AMBIENT_TEMPERATURE ハードウェア 周囲の室温を摂氏(°C)単位で測定します。下記の注をご覧ください。 気温のモニタリング。
TYPE_GRAVITY ソフトウェアまたはハードウェア デバイスにかかる 3 本の物理軸(x、y、z)方向の重力を m/s2 単位で測定します。 動きの検出(シェイク、ティルトなど)。
TYPE_GYROSCOPE ハードウェア 3 本の物理軸(x、y、z)に対するデバイスの回転速度を rad/s 単位で測定します。 回転の検出(スピン、ターンなど)。
TYPE_LIGHT ハードウェア 周囲光レベル(照度)をルクス単位で測定します。 画面の明るさの制御。
TYPE_LINEAR_ACCELERATION ソフトウェアまたはハードウェア デバイスにかかる 3 本の物理軸(x、y、z)方向の加速力(重力を除く)を m/s2 単位で測定します。 1 軸方向の加速度のモニタリング。
TYPE_MAGNETIC_FIELD ハードウェア 3 本の物理軸(x、y、z)方向の地磁気をマイクロテスラ単位で測定します。 コンパスの作成。
TYPE_ORIENTATION ソフトウェア 3 本の物理軸(x、y、z)に対するデバイスの回転角度を測定します。API レベル 3 以降では、重力センサーと地磁気センサーを getRotationMatrix() メソッドと組み合わせることで、デバイスの勾配行列と回転行列を求めることができます。 デバイスの姿勢の特定。
TYPE_PRESSURE ハードウェア 周囲の気圧をヘクトパスカル(ミリバール)単位で測定します。 気圧変化のモニタリング。
TYPE_PROXIMITY ハードウェア デバイスの表示画面に対する物体の近さを cm 単位で測定します。通常このセンサーは、モバイル デバイスが人の耳の近くにあるかどうかを判断するために使用します。 通話中のスマートフォンの位置。
TYPE_RELATIVE_HUMIDITY ハードウェア 周囲の相対湿度を百分率(%)で測定します。 露点温度、絶対湿度、相対湿度のモニタリング。
TYPE_ROTATION_VECTOR ソフトウェアまたはハードウェア デバイスの回転ベクトルの 3 要素を使用してデバイスの向きを測定します。 動きの検出と回転の検出。
TYPE_TEMPERATURE ハードウェア デバイスの温度を摂氏(°C)単位で測定します。デバイスによって実装方法が異なります。このセンサーは、API レベル 14 で TYPE_AMBIENT_TEMPERATURE センサーに置き換えられました。 温度のモニタリング。

センサー フレームワーク

Android センサー フレームワークを使用することにより、上記のセンサーから測定データを取得できます。センサー フレームワークは android.hardware パッケージに含まれており、以下のクラスとインターフェースが用意されています。

SensorManager
このクラスを使用して、センサー サービスのインスタンスを作成できます。このクラスには、センサーの利用とリストアップ、センサー イベント リスナーの登録と登録解除、向き情報の取得のための、さまざまなメソッドが用意されています。さらに、センサーの精度の報告、データ取得速度の設定、センサーの調整に使用するセンサー定数も用意されています。
Sensor
このクラスを使用して、特定のセンサーのインスタンスを作成できます。このクラスには、センサーの性能を判定するためのさまざまなメソッドが用意されています。
SensorEvent
このクラスを使用して、センサー イベントの情報を伝えるためのセンサー イベント オブジェクトがシステムにより作成されます。センサー イベント オブジェクトには、センサーからの測定データ、イベントを生成したセンサーのタイプ、データの精度、イベントのタイムスタンプといった情報が含まれます。
SensorEventListener
このインターフェースを使用して、センサー値やセンサー精度が変化したときに通知(センサー イベント)を受け取るための、2 つのコールバック メソッドを作成できます。

通常のアプリでは、上記のセンサー関連 API を使用して、次の 2 つの基本的なタスクを行います。

  • センサーとセンサー性能の特定

    特定のタイプや性能のセンサーに依存する機能を備えたアプリの場合、ランタイムにセンサーやセンサー性能を判別できると便利です。たとえば、デバイス上に存在するセンサーをすべて特定して、存在しないセンサーを必要とするアプリ機能は無効にできます。また、同じタイプのセンサーをすべて特定して、その中からアプリに最適なパフォーマンスをもたらすセンサーを選択することもできます。

  • センサー イベントのモニタリング

    センサーから測定データを取得するには、センサー イベントをモニタリングします。センサーによって測定中のパラメータの変化が検出されるたびに、センサー イベントが発生します。センサー イベントからは、イベントをトリガーしたセンサーの名前、イベントのタイムスタンプ、イベントの精度、イベントをトリガーした測定データ、の 4 つの情報を取得できます。

センサー対応状況

利用可能なセンサーは、デバイスによっても、Android のバージョンによっても異なります。これは、Android センサーが数回のプラットフォーム リリースにわたって導入されてきたためです。たとえば、多くのセンサーが Android 1.5(API レベル 3)で導入されましたが、その一部は Android 2.3(API レベル 9)未満では実装されず使用できませんでした。Android 2.3(API レベル 9)や Android 4.0(API レベル 14)で新たに導入されたセンサーもあります。また、2 つのセンサーはサポートが終了し、より適切な新しいセンサーに置き換えられました。

表 2 に、プラットフォームごとのセンサー対応状況をまとめます。センサーが変更されたプラットフォームは 4 種類のため、ここにはそれらのみを示します。サポート終了となっているセンサーも、Android の上位互換性ポリシーにのっとり、以降のプラットフォームでも利用できます(デバイスに該当のセンサーが存在する場合)。

表 2. プラットフォームごとのセンサー対応状況

センサー Android 4.0
(API Level 14)
Android 2.3
(API Level 9)
Android 2.2
(API Level 8)
Android 1.5
(API Level 3)
TYPE_ACCELEROMETER はい
TYPE_AMBIENT_TEMPERATURE × × なし
TYPE_GRAVITY はい × なし
TYPE_GYROSCOPE はい ×1 ×1
TYPE_LIGHT はい
TYPE_LINEAR_ACCELERATION × なし
TYPE_MAGNETIC_FIELD はい はい
TYPE_ORIENTATION 2 2 2
TYPE_PRESSURE ×1 ×1
TYPE_PROXIMITY はい
TYPE_RELATIVE_HUMIDITY × × なし
TYPE_ROTATION_VECTOR はい × なし
TYPE_TEMPERATURE 2 はい

1 Android 1.5(API レベル 3)で導入されましたが、Android 2.3(API レベル 9)未満では使用できません。

2 使用はできますが、サポートは終了しています。

センサーとセンサー性能の特定

Android センサー フレームワークには、ランタイムにデバイス上のセンサーを簡単に特定できるメソッドがいくつか用意されています。また、この API には、各センサーの性能(最大測定範囲、解像度、所要電力など)を判定するためのメソッドも用意されています。

デバイス上のセンサーを特定するには、まずセンサー サービスへの参照を取得する必要があります。これを行うには、SENSOR_SERVICE を引数にして getSystemService() メソッドを呼び出すことで、SensorManager クラスのインスタンスを作成します。例:

Kotlin

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

Java

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

次に、TYPE_ALL 定数を引数にして getSensorList() メソッドを呼び出すことで、デバイス上のすべてのセンサーをリストアップできます。例:

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() メソッドを呼び出すことで、そのタイプのセンサーがデバイス上に存在するかどうかを判定することもできます。デバイス上に同じタイプのセンサーが複数ある場合は、その中の 1 つをデフォルト センサーに指定する必要があります。メソッドの呼び出しで指定したセンサータイプにデフォルト センサーが存在しない場合は、null が返されます。これは、デバイス上にそのタイプのセンサーが存在しないことを意味します。たとえば、次のコードでは、デバイス上に磁力計があるかどうかを確認しています。

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() メソッドを使用すれば、センサーの所要電力を取得できます。

センサーのメーカーやバージョンに応じてアプリを最適化する場合に特に役立つパブリック メソッドが 2 つあります。たとえば、アプリで傾斜やシェイクなどのユーザー ジェスチャーをモニタリングする必要がある場合は、特定のベンダーの重力センサーを搭載した新しいデバイス用に 1 つのデータ フィルタリング ルールと最適化を作成できます。また、重力センサーを搭載しておらず、加速度センサーのみを搭載したデバイス用に別のデータ フィルタリング ルールと最適化を作成できます。次のサンプルコードに、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 インターフェースから起動される 2 つのコールバック メソッド、つまり onAccuracyChanged()onSensorChanged() を実装する必要があります。次の場合には必ず、Android システムによりこれらのメソッドが呼び出されます。

  • センサーの精度が変わった場合

    この場合、システムにより onAccuracyChanged() メソッドが呼び出されます。これにより、変更された Sensor オブジェクトへの参照とセンサーの変更後の精度を取得できます。精度は、4 つのステータス定数(SENSOR_STATUS_ACCURACY_LOWSENSOR_STATUS_ACCURACY_MEDIUMSENSOR_STATUS_ACCURACY_HIGHSENSOR_STATUS_UNRELIABLE)のいずれかで表されます。

  • センサーから新しい値が報告された場合

    この場合、システムにより onSensorChanged() メソッドが呼び出されます。これにより、SensorEvent オブジェクトを取得できます。SensorEvent オブジェクトには、データの精度、データを生成したセンサー、データが生成されたときのタイムスタンプ、センサーにより新たに記録されたデータといった、最新のセンサーデータに関する情報が含まれています。

次のコードは、onSensorChanged() メソッドを使って光センサーからのデータをモニタリングする方法を示しています。この例では、main.xml ファイルで sensor_data として定義した TextView にセンサーの測定データを表示します。

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);
    }
}

この例では、デフォルトのデータ遅延(SENSOR_DELAY_NORMAL)を、registerListener() メソッドが呼び出されたときに指定しています。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 搭載デバイスを任意のセンサー構成にできます。その結果、多様なセンサーを備えたさまざまな構成のデバイスが存在しえます。アプリで特定のタイプのセンサーを必要とする場合は、アプリを正常に動作させるために、そのセンサーがデバイスに存在することを確認する必要があります。

デバイスに特定のセンサーが存在することを確認するには、次の 2 つの方法があります。

  • ランタイムにセンサーを検出し、その結果に応じてアプリの機能を有効または無効にする。
  • Google Play フィルタを使用して、特定のセンサー構成のデバイスのみをターゲットにする。

それぞれの方法について、以下のセクションで説明します。

ランタイムにおけるセンサーの検出

アプリで特定のタイプのセンサーを使用しているものの必須ではない場合は、センサー フレームワークを使用してランタイムにセンサーを検出し、必要に応じてアプリの機能を有効または無効にします。たとえば、あるナビアプリでは、温度センサー、圧力センサー、GPS センサー、地磁気センサーを使用して温度、気圧、場所、方角を表示するとします。デバイスに圧力センサーがない場合は、センサー フレームワークを使用してランタイムにそのことを検出し、気圧を表示する UI の部分を無効にできます。たとえば、次のコードでは、デバイスに圧力センサーがあるかどうかを確認しています。

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)以降をターゲットにしている場合、ユーザーの機密に該当する可能性がある情報を保護するため、特定のモーション センサーや位置センサーからのデータのリフレッシュ レートに制限が課されます。このデータには、デバイスの加速度計ジャイロスコープ地磁気センサーによって記録された値が含まれます。

リフレッシュ レートの制限は、センサーデータへのアクセス方法によって異なります。

  • registerListener() メソッドを呼び出してセンサーイベントをモニタリングする場合、センサーのサンプリング レートは 200 Hz に制限されます。これは、registerListener() メソッドのオーバーロードされたすべてのバリアントに適用されます。
  • SensorDirectChannel クラスを使用する場合、センサーのサンプリング レートは RATE_NORMAL(通常は約 50 Hz)に制限されます。

アプリがより高いレートでモーション センサーデータを収集する必要がある場合は、次のコード スニペットに示すように、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 には、センサー(加速度センサー、温度センサー、磁力計、近接センサー、光センサーなど)のテストに使用できる仮想センサー コントロールが含まれています。

Android Emulator では、SdkControllerSensor アプリが実行されている Android デバイスとの接続が使用されます。なお、このアプリは Android 4.0(API レベル 14)以上のデバイスでのみ利用できます(Android 4.0 搭載デバイスの場合は、Revision 2 がインストールされている必要があります)。SdkControllerSensor アプリによって、デバイス上のセンサーの変化がモニタリングされ Android Emulator に送信されます。Android Emulator は、デバイス上のセンサーから送られた最新の値に基づいて更新されます。

SdkControllerSensor アプリのソースコードは、次の場所で確認できます。

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

デバイスと Android Emulator 間でデータを転送する手順は次のとおりです。

  1. デバイス側で USB デバッグが有効になっていることを確認します。
  2. USB ケーブルを使用してデバイスを開発用マシンに接続します。
  3. デバイス側で SdkControllerSensor アプリを起動します。
  4. SdkControllerSensor アプリで、エミュレートするセンサーを選択します。
  5. 次の adb コマンドを実行します。

  6. $ adb forward tcp:1968 tcp:1968
    
  7. Android Emulator を起動します。これで、デバイスの動きによる変化が Android Emulator に反映されるようになります。

注: 物理デバイスに加えた動きが Android Emulator に反映されない場合は、手順 5 の adb コマンドを再度実行します。

詳細については、Android Emulator のガイドをご覧ください。

onSensorChanged() メソッドをブロックしない

センサーデータは高速に変化する可能性があります。つまり、システムによって onSensorChanged(SensorEvent) メソッドが頻繁に呼び出される可能性があります。onSensorChanged(SensorEvent) メソッド内で行うことはできるだけ少なくして、これをブロックしないようにするのがベスト プラクティスです。アプリでセンサーデータのフィルタや換算が必要な場合は、onSensorChanged(SensorEvent) メソッドの外側でその処理を行います。

サポートが終了したメソッドやセンサータイプを使用しない

いくつかのメソッドや定数はサポートが終了しています。具体的には、TYPE_ORIENTATION センサータイプはサポートが終了しています。向きのデータを取得するには、代わりに getOrientation() メソッドを使用します。同様に、TYPE_TEMPERATURE センサータイプもサポートが終了しています。Android 4.0 を搭載しているデバイスでは、代わりに TYPE_AMBIENT_TEMPERATURE センサータイプを使用します。

センサーを使用する前に確認する

センサーからデータを取得しようとする前に、必ずそのセンサーがデバイス上に存在することを確認します。一般的なセンサーだからといって、単純に存在すると決めつけないでください。どのようなセンサーについても、デバイス メーカーがそれをデバイスに組み込まなければならないという決まりはありません。

センサーの遅延は慎重に選択する

registerListener() メソッドでセンサーを登録する際には、アプリや用途に合った配信速度を選択してください。センサーからは、極めて高速にデータが提供される可能性があります。不要なデータまで配信されるようにすると、システム リソースや電池を無駄に消費することになります。