Обзор датчиков

Большинство устройств Android оснащены встроенными датчиками, которые измеряют движение, ориентацию и различные условия окружающей среды. Эти датчики способны предоставлять первичные данные с высокой точностью и аккуратностью и полезны, если вы хотите отслеживать трехмерное движение или позиционирование устройства или хотите отслеживать изменения в окружающей среде вблизи устройства. Например, игра может отслеживать показания датчика силы тяжести, чтобы распознавать сложные жесты и движения пользователя, такие как наклон, встряхивание, вращение или взмах. Аналогичным образом приложение для прогноза погоды может использовать датчики температуры и влажности устройства для расчета и сообщения точки росы, а приложение для путешествий может использовать датчик геомагнитного поля и акселерометр для сообщения направления по компасу.

Платформа Android поддерживает три основные категории датчиков:

  • Датчики движения

    Эти датчики измеряют силу ускорения и вращения по трем осям. К этой категории относятся акселерометры, датчики силы тяжести, гироскопы и датчики вектора вращения.

  • Датчики параметров окружающей среды

    Эти датчики измеряют различные параметры окружающей среды, такие как температура и давление окружающего воздуха, освещенность и влажность. К этой категории относятся барометры, фотометры и термометры.

  • Датчики положения

    Эти датчики определяют физическое положение устройства. К этой категории относятся датчики ориентации и магнитометры.

Вы можете получить доступ к датчикам, доступным на устройстве, и получить необработанные данные датчиков с помощью фреймворка датчиков Android. Фреймворк датчиков предоставляет несколько классов и интерфейсов, которые помогают выполнять различные задачи, связанные с датчиками. Например, с помощью фреймворка датчиков можно:

  • Определять, какие датчики доступны на устройстве.
  • Определите возможности отдельного датчика, такие как его максимальный диапазон, производитель, требования к питанию и разрешение.
  • Получайте необработанные данные датчиков и задавайте минимальную частоту их получения.
  • Регистрировать и отменять регистрацию прослушивателей событий датчиков, которые отслеживают изменения в датчиках.

В этой статье представлен обзор датчиков, доступных на платформе Android. и фреймворком датчиков.

Общие сведения о датчиках

Фреймворк датчиков Android позволяет получать доступ к датчикам разных типов. Некоторые из этих датчиков являются аппаратными, а некоторые – программными. Аппаратные датчики – это физические компоненты, встроенные в телефон или планшет. Они получают данные, непосредственно измеряя определенные свойства окружающей среды, такие как ускорение, напряженность геомагнитного поля или угловое изменение. Программные датчики не являются физическими устройствами, хотя они имитируют аппаратные датчики. Программные датчики получают данные от одного или нескольких аппаратных датчиков. Иногда их называют виртуальными или синтетическими датчиками. Примерами программных датчиков являются датчик линейного ускорения и датчик силы тяжести. В таблице 1 перечислены датчики, поддерживаемые платформой Android.

Лишь немногие устройства Android оснащены всеми типами датчиков. Например, у большинства телефонов и планшетов есть акселерометр и магнитометр, но барометры и термометры встречаются реже. Кроме того, на устройстве может быть несколько датчиков одного типа. Например, на устройстве может быть два датчика силы тяжести с разными диапазонами.

Таблица 1. Типы датчиков, поддерживаемые платформой Android.

Датчик Тип Описание Как использовать аннотации
TYPE_ACCELEROMETER Аппаратное обеспечение Измеряет силу ускорения в м/с2, которая применяется к устройству по всем трем физическим осям (x, y и z), включая силу тяжести. Обнаружение движения (встряхивание, наклон и т. д.).
TYPE_AMBIENT_TEMPERATURE Аппаратное обеспечение Измеряет температуру в помещении в градусах Цельсия (°C). См. примечание ниже. Отслеживание температуры воздуха.
TYPE_GRAVITY Программное или аппаратное обеспечение Измеряет силу тяжести в м/с2, которая применяется к устройству по всем трем физическим осям (x, y, z). Обнаружение движения (встряхивание, наклон и т. д.).
TYPE_GYROSCOPE Аппаратное обеспечение Измеряет скорость вращения устройства в рад/с вокруг каждой из трех физических осей (x, y и z). Обнаружение вращения (поворот, вращение и т. д.).
TYPE_LIGHT Аппаратное обеспечение Измеряет уровень окружающего света (освещенности) в люксах. Управление яркостью экрана.
TYPE_LINEAR_ACCELERATION Программное или аппаратное обеспечение Измеряет силу ускорения в м/с2, которая применяется к устройству по всем трем физическим осям (x, y и z), исключая силу тяжести. Отслеживание ускорения по одной оси.
TYPE_MAGNETIC_FIELD Аппаратное обеспечение Измеряет геомагнитное поле по трем физическим осям (x, y, z) в микротеслах. Создание компаса.
TYPE_ORIENTATION Программное обеспечение Измеряет угол поворота устройства вокруг трех физических осей (x, y, z). Начиная с уровня API 3, вы можете получить матрицу наклона и матрицу вращения для устройства, используя датчик силы тяжести и датчик геомагнитного поля в сочетании с методом getRotationMatrix(). Определение местоположения устройства.
TYPE_PRESSURE Аппаратное обеспечение Измеряет атмосферное давление в гПа или мбар. Отслеживание изменений атмосферного давления.
TYPE_PROXIMITY Аппаратное обеспечение Измеряет близость объекта в сантиметрах относительно экрана устройства. Этот датчик обычно используется, чтобы определить, поднесен ли мобильный телефон к уху человека. Положение телефона во время звонка.
TYPE_RELATIVE_HUMIDITY Аппаратное обеспечение Измеряет относительную влажность воздуха в процентах (%). Отслеживание точки росы, абсолютной и относительной влажности.
TYPE_ROTATION_VECTOR Программное или аппаратное обеспечение Определяет ориентацию устройства, предоставляя три элемента вектора вращения устройства. Обнаружение движения и поворота.
TYPE_TEMPERATURE Аппаратное обеспечение Измеряет температуру устройства в градусах Цельсия (°C). Реализация этого датчика различается на разных устройствах. В уровне API 14 он был заменен датчиком TYPE_AMBIENT_TEMPERATURE. Отслеживание температуры.

Sensor Framework

Вы можете получить доступ к этим датчикам и получать необработанные данные датчиков, используя фреймворк датчиков 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 Да – – –
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 также предоставляет методы, позволяющие определить возможности каждого датчика, такие как его максимальный диапазон, разрешение и требования к питанию.

Чтобы определить, какие датчики есть на устройстве, сначала нужно получить ссылку на сервис датчиков. Для этого создайте экземпляр класса SensorManager, вызвав метод getSystemService() и передав аргумент SENSOR_SERVICE. Пример:

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() и передав константу типа для конкретного датчика. Если на устройстве есть несколько датчиков одного типа, один из них должен быть назначен датчиком по умолчанию. Если датчик по умолчанию для заданного типа датчика не существует, вызов метода возвращает значение 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 не требует от производителей устройств встраивать в них какие-либо определенные типы датчиков, поэтому устройства могут иметь широкий спектр конфигураций датчиков.

Помимо получения списка датчиков, установленных на устройстве, вы можете использовать общедоступные методы класса 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 вызывает эти методы в следующих случаях:

  • Точность датчика меняется.

    В этом случае система вызывает метод onAccuracyChanged(), предоставляя вам ссылку на измененный объект Sensor и новую точность датчика. Точность определяется одной из четырех констант: SENSOR_STATUS_ACCURACY_LOW, SENSOR_STATUS_ACCURACY_MEDIUM, SENSOR_STATUS_ACCURACY_HIGH или SENSOR_STATUS_UNRELIABLE.

  • Датчик сообщает о новом значении.

    В этом случае система вызывает метод onSensorChanged(), предоставляя вам объект SensorEvent. Объект SensorEvent содержит информацию о новых данных датчика, в том числе: точность данных, датчик, который сгенерировал данные, временную метку, когда данные были сгенерированы, и новые данные, которые записал датчик.

В следующем примере кода показано, как использовать метод onSensorChanged() для отслеживания данных от датчика освещенности. В этом примере необработанные данные датчика отображаются в элементе TextView, который определен в XML-файле main.xml как sensor_data.

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 нет стандартной конфигурации датчиков, поэтому производители могут использовать любые датчики в своих устройствах. В результате устройства могут включать в себя разнообразные датчики в широком диапазоне конфигураций. Если для работы приложения требуется определенный тип датчика, необходимо убедиться, что он присутствует на устройстве.

Убедиться, что на устройстве есть нужный датчик, можно двумя способами:

  • Обнаруживать датчики во время выполнения и включать или отключать функции приложения по мере необходимости.
  • Используйте фильтры 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". Это позволит устанавливать приложение на устройства, у которых нет нужного датчика. Это также является лучшей практикой управления проектами, которая помогает вам отслеживать функции, используемые вашим приложением. Если приложение использует определенный датчик, но может работать и без него, то во время выполнения приложение должно определить наличие датчика и включить или отключить соответствующие функции.

Система координат датчика

Как правило, для представления значений данных в платформе датчиков используется стандартная трехосевая система координат. Для большинства датчиков система координат определяется относительно экрана устройства, когда устройство удерживается в ориентации по умолчанию (см. рисунок 1). Когда устройство находится в положении по умолчанию, ось X горизонтальна и направлена вправо, ось Y вертикальна и направлена вверх, а ось Z направлена от экрана. В этой системе координаты за экраном имеют отрицательные значения Z. Эта система координат используется следующими датчиками:

Рисунок 1. Система координат (относительно устройства), используемая Sensor API.

Самое важное, что нужно знать об этой системе координат, – это то, что оси не меняются местами при изменении ориентации экрана устройства. То есть система координат датчика никогда не меняется при перемещении устройства. Это соответствует поведению системы координат OpenGL.

Также не следует исходить из того, что ориентация по умолчанию для устройства – вертикальная. Для многих планшетов естественной является горизонтальная ориентация. А система координат датчика всегда основана на естественной ориентации устройства.

Наконец, если ваше приложение сопоставляет данные датчиков с изображением на экране, вам нужно использовать метод getRotation(), чтобы определить поворот экрана, а затем метод remapCoordinateSystem(), чтобы сопоставить координаты датчиков с координатами экрана. Это нужно сделать, даже если в манифесте указана только книжная ориентация.

Примечание. Некоторые датчики и методы используют систему координат, которая связана с системой отсчета Земли (в отличие от системы отсчета устройства). Эти датчики и методы возвращают данные, которые представляют движение устройства или его положение относительно Земли. Дополнительную информацию можно найти в описании методов getOrientation() и getRotationMatrix(), а также в статьях Orientation Sensor и Rotation Vector Sensor.

Ограничение частоты запросов датчиков

Чтобы защитить потенциально конфиденциальную информацию о пользователях, в Android 12 (уровень API 31) и более поздних версиях ОС ограничена частота обновления данных, поступающих от некоторых датчиков движения и положения. К ним относятся значения, записанные акселерометром, гироскопом и датчиком геомагнитного поля устройства.

Ограничение частоты обновления зависит от того, как вы получаете доступ к данным датчиков:

  • Если вы вызываете метод registerListener(), чтобы отслеживать события датчиков, частота дискретизации датчика ограничивается 200 Гц. Это относится ко всем перегруженным вариантам метода registerListener().
  • Если вы используете класс SensorDirectChannel, частота дискретизации датчика ограничена значением RATE_NORMAL, которое обычно составляет около 50 Гц.

Если вашему приложению нужно собирать данные датчиков движения с более высокой частотой, необходимо объявить разрешение 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) или более поздней версии для приложений, работающих в фоновом режиме, действуют следующие ограничения:

  • Датчики, использующие режим непрерывных отчетов, например акселерометры и гироскопы, не получают события.
  • Датчики, использующие режимы отчетов on-change или one-shot, не получают события.

Учитывая эти ограничения, лучше всего обнаруживать события датчиков, когда приложение находится на переднем плане или в рамках активной службы.

Отмена регистрации прослушивателей датчиков

Не забудьте отменить регистрацию прослушивателя датчика, когда вы закончите использовать датчик или когда его активность приостановится. Если прослушиватель датчика зарегистрирован, а его действие приостановлено, датчик продолжит собирать данные и использовать ресурсы батареи, пока вы не отмените его регистрацию. В примере кода ниже показано, как использовать метод 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

В Android Emulator есть набор элементов управления виртуальными датчиками, который позволяет тестировать акселерометр, датчик температуры окружающей среды, магнитометр, датчик приближения, датчик освещенности и т. д.

Эмулятор использует подключение к устройству Android, на котором запущено приложение SdkControllerSensor. Обратите внимание, что это приложение доступно только на устройствах с 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. Запустите эмулятор. Теперь вы можете применять преобразования к эмулятору, перемещая устройство.

Примечание. Если движения физического устройства не передаются в эмулятор, попробуйте снова выполнить команду adb из шага 5.

Дополнительную информацию можно найти в руководстве по Android Emulator.

Не блокируйте метод onSensorChanged()

Данные датчиков могут быстро меняться, поэтому система может часто вызывать метод onSensorChanged(SensorEvent). Рекомендуем выполнять в методе onSensorChanged(SensorEvent) как можно меньше действий, чтобы не блокировать его. Если в приложении требуется фильтрация или сокращение данных датчиков, это нужно делать за пределами метода onSensorChanged(SensorEvent).

Не используйте устаревшие методы и типы датчиков

Некоторые методы и константы больше не поддерживаются. В частности, больше не поддерживается тип датчика TYPE_ORIENTATION. Чтобы получить данные об ориентации, используйте метод getOrientation(). Тип датчика TYPE_TEMPERATURE также больше не поддерживается. На устройствах с Android 4.0 следует использовать тип датчика TYPE_AMBIENT_TEMPERATURE.

Проверьте датчики перед использованием

Прежде чем пытаться получить данные с датчика, всегда проверяйте, есть ли он на устройстве. Не предполагайте, что датчик существует только потому, что он часто используется. Производители устройств не обязаны оснащать свои устройства какими-либо конкретными датчиками.

Тщательно выбирайте задержки датчиков

При регистрации датчика с помощью метода registerListener() выберите частоту передачи данных, подходящую для вашего приложения или сценария использования. Датчики могут передавать данные с очень высокой частотой. Если система будет отправлять лишние данные, которые вам не нужны, это приведет к напрасному расходу ресурсов и заряда батареи.