センサーの概要

ほとんどの Android 搭載デバイスには、動き、向き、さまざまな環境条件を測定するセンサーが組み込まれています。これらのセンサーは、高精度かつ正確な元データを提供できるため、デバイスの 3 次元の動きや位置をモニタリングする場合や、デバイス周辺の環境の変化をモニタリングする場合に便利です。たとえば、ゲームでは、デバイスの重力センサーの読み取り値を追跡して、傾き、揺れ、回転、スイングなどの複雑なユーザー ジェスチャーやモーションを推測できます。同様に、天気アプリはデバイスの温度センサーと湿度センサーを使用して露点温度を計算して報告したり、旅行アプリは地磁気センサーと加速度計を使用してコンパスの方位を報告したりできます。

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

  • モーション センサー

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

  • 環境センサー

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

  • 位置センサー

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

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

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

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

センサーの概要

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

すべてのタイプのセンサーを備えた Android 搭載デバイスはまれにしかありません。たとえば、ほとんどのハンドセット デバイスとタブレットには加速度計と磁力計が搭載されていますが、気圧計や温度計が搭載されているデバイスは少数です。また、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)すべてについて、周囲の地磁気を μT 単位で測定します。 コンパスの作成。
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 には、各センサーの機能(最大範囲、解像度、電力要件など)を特定できるメソッドも用意されています。

デバイスに搭載されているセンサーを特定するには、まずセンサー サービスへの参照を取得する必要があります。これを行うには、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_GYROSCOPE、TYPE_LINEAR_ACCELERATION、TYPE_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 つ作成し、重力センサーを搭載せず、加速度計のみを搭載したデバイス向けに、データ フィルタリング ルールと最適化のセットをもう 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.
    }
}

もう 1 つの便利なメソッドは getMinDelay() メソッドです。このメソッドは、センサーがデータの検出に使用できる最小時間間隔(マイクロ秒単位)を返します。getMinDelay() メソッドに対してゼロ以外の値を返すセンサーは、ストリーミング センサーです。ストリーミング センサーは、一定の間隔でデータを感知するセンサーで、Android 2.3(API レベル 9)で導入されました。getMinDelay() メソッドを呼び出したときにセンサーが 0 を返した場合、そのセンサーはストリーミング センサーではありません。検出しているパラメータに変更があった場合にのみデータを報告するためです。

getMinDelay() メソッドは、センサーがデータを取得できる最大レートを決定できるため、便利です。アプリの特定の機能で高いデータ取得率やストリーミング センサーが必要な場合は、このメソッドを使用して、センサーがそれらの要件を満たしているかどうかを判断し、それに応じてアプリの関連機能を有効または無効にできます。

注意: センサーの最大データ取得レートは、センサー フレームワークがアプリにセンサーデータを配信するレートとは必ずしも一致しません。センサー フレームワークはセンサー イベントを通じてデータを報告します。アプリがセンサー イベントを受信する頻度は、いくつかの要因によって異なります。詳細については、センサー イベントのモニタリングをご覧ください。

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

未加工のセンサーデータをモニタリングするには、SensorEventListener インターフェースを介して公開される 2 つのコールバック メソッド(onAccuracyChanged() と onSensorChanged())を実装する必要があります。Android システムは、次の状況が発生するたびにこれらのメソッドを呼び出します。

  • センサーの精度が変化する。

    この場合、システムは onAccuracyChanged() メソッドを呼び出し、変更された Sensor オブジェクトへの参照とセンサーの新しい精度を提供します。精度は、SENSOR_STATUS_ACCURACY_LOW、SENSOR_STATUS_ACCURACY_MEDIUM、SENSOR_STATUS_ACCURACY_HIGH、SENSOR_STATUS_UNRELIABLE の 4 つのステータス定数のいずれかで表されます。

  • センサーが新しい値を報告する。

    この場合、システムは 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 座標系の動作と同じです。

もう 1 つ理解しておくべき点は、アプリはデバイスの自然な(デフォルトの)向きが縦向きであると想定してはならないということです。多くのタブレット デバイスでは、自然な向きは横向きになっています。また、センサーの座標系は常にデバイスの自然な向きに基づいています。

最後に、アプリがセンサーデータを画面上の表示と一致させる場合は、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 には、加速度計、周囲温度、磁力計、近接、光などのセンサーをテストできる一連の仮想センサー コントロールが含まれています。

エミュレータは、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. SdkControllerSensor アプリで、エミュレートするセンサーを選択します。
  5. 次の adb コマンドを実行します。

  6. $ adb forward tcp:1968 tcp:1968
    
  7. 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() メソッドでセンサーを登録する場合は、アプリまたはユースケースに適した配信レートを選択してください。センサーからは、極めて高速にデータが提供される可能性があります。不要な追加データをシステムが送信できるようにすると、システム リソースが無駄になり、バッテリー電力が消費されます。