センサーの概要

ほとんどの 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)すべての周囲の地磁場を μ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_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 つが特に役立つのは、メーカーのセンサーやセンサーのバージョンが異なるためにアプリを最適化する場合に特に便利です。たとえば、アプリで傾斜やシェイクなどのユーザー操作を監視する必要がある場合、特定のベンダーの重力センサーを搭載した新しいデバイス向けのデータ フィルタリング ルールと最適化のセットと、重力センサーがなく加速度計しかないデバイス向けのデータ フィルタリング ルールと最適化のセットを作成できます。次のコードサンプルは、これを行うために 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() メソッドを呼び出したときにセンサーがゼロを返した場合、そのセンサーはストリーミング センサーではありません。これは、センサーが検出するパラメータに変化があったときにのみデータを報告するためです。

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. Sensor 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 には、加速度計、周囲温度、磁力計、近接、光などのセンサーをテストできる仮想センサー コントロールのセットが用意されています。

エミュレータは、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() メソッドでセンサーを登録する場合は、アプリケーションやユースケースに適した配信率を選択してください。センサーからは、極めて高速にデータが提供される可能性があります。不要なデータの送信を許可すると、システム リソースが浪費され、バッテリーも消費されます。