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

Большинство устройств на базе 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). Реализация этого датчика различается на разных устройствах, и этот датчик был заменен датчиком TYPE_AMBIENT_TEMPERATURE в уровне API 14. Мониторинг температуры.

Сенсорная платформа

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

Датчик Андроид 4.0
(уровень API 14)
Андроид 2.3
(API-уровень 9)
Андроид 2.2
(уровень API 8)
Андроид 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 . Например:

Котлин

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

Ява

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

Далее вы можете получить список всех датчиков на устройстве, вызвав метод getSensorList() и используя константу TYPE_ALL . Например:

Котлин

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

Ява

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

Если вы хотите перечислить все датчики данного типа, вы можете использовать другую константу вместо TYPE_ALL например TYPE_GYROSCOPE , TYPE_LINEAR_ACCELERATION или TYPE_GRAVITY .

Вы также можете определить, существует ли на устройстве датчик определенного типа, используя метод getDefaultSensor() и передав константу типа для определенного датчика. Если устройство имеет более одного датчика данного типа, один из датчиков должен быть назначен датчиком по умолчанию. Если для данного типа датчика не существует датчика по умолчанию, вызов метода возвращает значение null, что означает, что на устройстве нет датчика этого типа. Например, следующий код проверяет, есть ли на устройстве магнитометр:

Котлин

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.
}

Ява

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

Примечание. Android не требует от производителей устройств встраивать в свои устройства под управлением Android какие-либо датчики определенного типа, поэтому устройства могут иметь широкий спектр конфигураций датчиков.

Помимо перечисления датчиков, находящихся на устройстве, вы можете использовать общедоступные методы класса Sensor для определения возможностей и атрибутов отдельных датчиков. Это полезно, если вы хотите, чтобы ваше приложение вело себя по-разному в зависимости от того, какие датчики или возможности датчиков доступны на устройстве. Например, вы можете использовать методы getResolution() и getMaximumRange() чтобы получить разрешение датчика и максимальный диапазон измерения. Вы также можете использовать метод getPower() для получения требований к питанию датчика.

Два общедоступных метода особенно полезны, если вы хотите оптимизировать свое приложение для датчиков разных производителей или разных версий датчиков. Например, если вашему приложению необходимо отслеживать жесты пользователя, такие как наклон и встряхивание, вы можете создать один набор правил и оптимизаций фильтрации данных для новых устройств, оснащенных датчиком силы тяжести конкретного производителя, а также другой набор правил фильтрации данных и оптимизаций для устройств. у которых нет датчика силы тяжести и есть только акселерометр. В следующем примере кода показано, как можно использовать для этого методы getVendor() и getVersion() . В этом примере мы ищем датчик силы тяжести, в котором в качестве поставщика указана компания Google LLC и номер версии 3. Если этот конкретный датчик отсутствует на устройстве, мы пытаемся использовать акселерометр.

Котлин

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

Ява

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 , который определен в файле main.xml как sensor_data .

Котлин

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

Ява

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

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

  • Обнаруживайте датчики во время выполнения и при необходимости включите или отключите функции приложения.
  • Используйте фильтры Google Play для определения устройств с определенными конфигурациями датчиков.

Каждый вариант обсуждается в следующих разделах.

Обнаружение датчиков во время выполнения

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

Котлин

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.
}

Ява

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 Гц. Это справедливо для всех перегруженных вариантов метода 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) или выше приложения, работающие в фоновом режиме, имеют следующие ограничения:

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

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

Обязательно отмените регистрацию прослушивателя датчика, когда вы закончите использовать датчик или когда активность датчика приостановится. Если прослушиватель датчика зарегистрирован и его активность приостановлена, датчик продолжит собирать данные и использовать ресурсы батареи, пока вы не отмените регистрацию датчика. Следующий код показывает, как использовать метод onPause() для отмены регистрации прослушивателя:

Котлин

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

Ява

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

Для получения дополнительной информации см. unregisterListener(SensorEventListener) .

Тестирование с помощью эмулятора Android

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

Эмулятор использует соединение с устройством 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 .

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

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

Избегайте использования устаревших методов или типов датчиков.

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

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

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

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

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

,

Большинство устройств на базе 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). Реализация этого датчика различается на разных устройствах, и этот датчик был заменен датчиком TYPE_AMBIENT_TEMPERATURE в уровне API 14. Мониторинг температуры.

Сенсорная платформа

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

Датчик Андроид 4.0
(уровень API 14)
Андроид 2.3
(API-уровень 9)
Андроид 2.2
(уровень API 8)
Андроид 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 . Например:

Котлин

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

Ява

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

Далее вы можете получить список всех датчиков на устройстве, вызвав метод getSensorList() и используя константу TYPE_ALL . Например:

Котлин

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

Ява

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

Если вы хотите перечислить все датчики данного типа, вы можете использовать другую константу вместо TYPE_ALL например TYPE_GYROSCOPE , TYPE_LINEAR_ACCELERATION или TYPE_GRAVITY .

Вы также можете определить, существует ли на устройстве датчик определенного типа, используя метод getDefaultSensor() и передав константу типа для определенного датчика. Если устройство имеет более одного датчика данного типа, один из датчиков должен быть назначен датчиком по умолчанию. Если для данного типа датчика не существует датчика по умолчанию, вызов метода возвращает значение null, что означает, что на устройстве нет датчика этого типа. Например, следующий код проверяет, есть ли на устройстве магнитометр:

Котлин

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.
}

Ява

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

Примечание. Android не требует от производителей устройств встраивать в свои устройства под управлением Android какие-либо датчики определенного типа, поэтому устройства могут иметь широкий спектр конфигураций датчиков.

Помимо перечисления датчиков, находящихся на устройстве, вы можете использовать общедоступные методы класса Sensor для определения возможностей и атрибутов отдельных датчиков. Это полезно, если вы хотите, чтобы ваше приложение вело себя по-разному в зависимости от того, какие датчики или возможности датчиков доступны на устройстве. Например, вы можете использовать методы getResolution() и getMaximumRange() чтобы получить разрешение датчика и максимальный диапазон измерения. Вы также можете использовать метод getPower() для получения требований к питанию датчика.

Два общедоступных метода особенно полезны, если вы хотите оптимизировать свое приложение для датчиков разных производителей или разных версий датчиков. Например, если вашему приложению необходимо отслеживать жесты пользователя, такие как наклон и встряхивание, вы можете создать один набор правил и оптимизаций фильтрации данных для новых устройств, оснащенных датчиком силы тяжести конкретного производителя, а также другой набор правил фильтрации данных и оптимизаций для устройств. у которых нет датчика силы тяжести и есть только акселерометр. В следующем примере кода показано, как можно использовать для этого методы getVendor() и getVersion() . В этом примере мы ищем датчик силы тяжести, в котором в качестве поставщика указана компания Google LLC и номер версии 3. Если этот конкретный датчик отсутствует на устройстве, мы пытаемся использовать акселерометр.

Котлин

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

Ява

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 , который определен в файле main.xml как sensor_data .

Котлин

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

Ява

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

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

  • Обнаруживайте датчики во время выполнения и при необходимости включите или отключите функции приложения.
  • Используйте фильтры Google Play для определения устройств с определенными конфигурациями датчиков.

Каждый вариант обсуждается в следующих разделах.

Обнаружение датчиков во время выполнения

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

Котлин

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.
}

Ява

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 Гц. Это справедливо для всех перегруженных вариантов метода 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) или выше приложения, работающие в фоновом режиме, имеют следующие ограничения:

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

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

Обязательно отмените регистрацию прослушивателя датчика, когда вы закончите использовать датчик или когда активность датчика приостановится. Если прослушиватель датчика зарегистрирован и его активность приостановлена, датчик продолжит собирать данные и использовать ресурсы батареи, пока вы не отмените регистрацию датчика. Следующий код показывает, как использовать метод onPause() для отмены регистрации прослушивателя:

Котлин

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

Ява

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

Для получения дополнительной информации см. unregisterListener(SensorEventListener) .

Тестирование с помощью эмулятора Android

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

Эмулятор использует соединение с устройством 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 .

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

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

Избегайте использования устаревших методов или типов датчиков.

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

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

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

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

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

,

Большинство устройств на базе 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). Реализация этого датчика различается на разных устройствах, и этот датчик был заменен датчиком TYPE_AMBIENT_TEMPERATURE в уровне API 14. Мониторинг температуры.

Сенсорная платформа

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

Датчик Андроид 4.0
(уровень API 14)
Андроид 2.3
(API-уровень 9)
Андроид 2.2
(уровень API 8)
Андроид 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 . Например:

Котлин

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

Ява

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

Далее вы можете получить список всех датчиков на устройстве, вызвав метод getSensorList() и используя константу TYPE_ALL . Например:

Котлин

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

Ява

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

Если вы хотите перечислить все датчики данного типа, вы можете использовать другую константу вместо TYPE_ALL например TYPE_GYROSCOPE , TYPE_LINEAR_ACCELERATION или TYPE_GRAVITY .

Вы также можете определить, существует ли на устройстве датчик определенного типа, используя метод getDefaultSensor() и передав константу типа для определенного датчика. Если устройство имеет более одного датчика данного типа, один из датчиков должен быть назначен датчиком по умолчанию. Если для данного типа датчика не существует датчика по умолчанию, вызов метода возвращает значение null, что означает, что на устройстве нет датчика этого типа. Например, следующий код проверяет, есть ли на устройстве магнитометр:

Котлин

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.
}

Ява

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

Примечание. Android не требует от производителей устройств встраивать в свои устройства под управлением Android какие-либо датчики определенного типа, поэтому устройства могут иметь широкий спектр конфигураций датчиков.

Помимо перечисления датчиков, находящихся на устройстве, вы можете использовать общедоступные методы класса Sensor для определения возможностей и атрибутов отдельных датчиков. Это полезно, если вы хотите, чтобы ваше приложение вело себя по-разному в зависимости от того, какие датчики или возможности датчиков доступны на устройстве. Например, вы можете использовать методы getResolution() и getMaximumRange() чтобы получить разрешение датчика и максимальный диапазон измерения. Вы также можете использовать метод getPower() для получения требований к питанию датчика.

Два общедоступных метода особенно полезны, если вы хотите оптимизировать свое приложение для датчиков разных производителей или разных версий датчиков. Например, если вашему приложению необходимо отслеживать жесты пользователя, такие как наклон и встряхивание, вы можете создать один набор правил и оптимизаций фильтрации данных для новых устройств, оснащенных датчиком силы тяжести конкретного производителя, а также другой набор правил фильтрации данных и оптимизаций для устройств. у которых нет датчика силы тяжести и есть только акселерометр. В следующем примере кода показано, как можно использовать для этого методы getVendor() и getVersion() . В этом примере мы ищем датчик силы тяжести, в котором в качестве поставщика указана компания Google LLC и номер версии 3. Если этот конкретный датчик отсутствует на устройстве, мы пытаемся использовать акселерометр.

Котлин

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

Ява

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 , который определен в файле main.xml как sensor_data .

Котлин

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

Ява

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

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

  • Обнаруживайте датчики во время выполнения и при необходимости включите или отключите функции приложения.
  • Используйте фильтры Google Play для определения устройств с определенными конфигурациями датчиков.

Каждый вариант обсуждается в следующих разделах.

Обнаружение датчиков во время выполнения

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

Котлин

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.
}

Ява

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 Гц. Это справедливо для всех перегруженных вариантов метода 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) или выше приложения, работающие в фоновом режиме, имеют следующие ограничения:

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

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

Обязательно отмените регистрацию прослушивателя датчика, когда вы закончите использовать датчик или когда активность датчика приостановится. Если прослушиватель датчика зарегистрирован и его активность приостановлена, датчик продолжит собирать данные и использовать ресурсы батареи, пока вы не отмените регистрацию датчика. Следующий код показывает, как использовать метод onPause() для отмены регистрации прослушивателя:

Котлин

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

Ява

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

Для получения дополнительной информации см. unregisterListener(SensorEventListener) .

Тестирование с помощью эмулятора Android

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

Эмулятор использует соединение с устройством 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 .

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

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

Избегайте использования устаревших методов или типов датчиков.

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

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

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

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

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

,

Большинство устройств на базе 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). Реализация этого датчика различается на разных устройствах, и этот датчик был заменен датчиком TYPE_AMBIENT_TEMPERATURE в уровне API 14. Мониторинг температуры.

Сенсорная платформа

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

Датчик Андроид 4.0
(уровень API 14)
Андроид 2.3
(API-уровень 9)
Андроид 2.2
(уровень API 8)
Андроид 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 . Например:

Котлин

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

Ява

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

Далее вы можете получить список всех датчиков на устройстве, вызвав метод getSensorList() и используя константу TYPE_ALL . Например:

Котлин

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

Ява

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

Если вы хотите перечислить все датчики данного типа, вы можете использовать другую константу вместо TYPE_ALL например TYPE_GYROSCOPE , TYPE_LINEAR_ACCELERATION или TYPE_GRAVITY .

Вы также можете определить, существует ли на устройстве датчик определенного типа, используя метод getDefaultSensor() и передав константу типа для определенного датчика. Если устройство имеет более одного датчика данного типа, один из датчиков должен быть назначен датчиком по умолчанию. Если для данного типа датчика не существует датчика по умолчанию, вызов метода возвращает значение null, что означает, что на устройстве нет датчика этого типа. Например, следующий код проверяет, есть ли на устройстве магнитометр:

Котлин

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.
}

Ява

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

Примечание. Android не требует от производителей устройств встраивать в свои устройства под управлением Android какие-либо датчики определенного типа, поэтому устройства могут иметь широкий спектр конфигураций датчиков.

Помимо перечисления датчиков, находящихся на устройстве, вы можете использовать общедоступные методы класса Sensor для определения возможностей и атрибутов отдельных датчиков. Это полезно, если вы хотите, чтобы ваше приложение вело себя по-разному в зависимости от того, какие датчики или возможности датчиков доступны на устройстве. Например, вы можете использовать методы getResolution() и getMaximumRange() чтобы получить разрешение датчика и максимальный диапазон измерения. Вы также можете использовать метод getPower() для получения требований к питанию датчика.

Два общедоступных метода особенно полезны, если вы хотите оптимизировать свое приложение для датчиков разных производителей или разных версий датчиков. Например, если вашему приложению необходимо отслеживать жесты пользователя, такие как наклон и встряхивание, вы можете создать один набор правил и оптимизаций фильтрации данных для новых устройств, оснащенных датчиком силы тяжести конкретного производителя, а также другой набор правил фильтрации данных и оптимизаций для устройств. у которых нет датчика силы тяжести и есть только акселерометр. В следующем примере кода показано, как можно использовать для этого методы getVendor() и getVersion() . В этом примере мы ищем датчик силы тяжести, в котором в качестве поставщика указана компания Google LLC и номер версии 3. Если этот конкретный датчик отсутствует на устройстве, мы пытаемся использовать акселерометр.

Котлин

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

Ява

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 , который определен в файле main.xml как sensor_data .

Котлин

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

Ява

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

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

  • Обнаруживайте датчики во время выполнения и при необходимости включите или отключите функции приложения.
  • Используйте фильтры Google Play для определения устройств с определенными конфигурациями датчиков.

Каждый вариант обсуждается в следующих разделах.

Обнаружение датчиков во время выполнения

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

Котлин

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.
}

Ява

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 Гц. Это справедливо для всех перегруженных вариантов метода 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) или выше приложения, работающие в фоновом режиме, имеют следующие ограничения:

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

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

Обязательно отмените регистрацию прослушивателя датчика, когда вы закончите использовать датчик или когда активность датчика приостановится. Если прослушиватель датчика зарегистрирован и его активность приостановлена, датчик продолжит собирать данные и использовать ресурсы батареи, пока вы не отмените регистрацию датчика. Следующий код показывает, как использовать метод onPause() для отмены регистрации прослушивателя:

Котлин

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

Ява

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

Для получения дополнительной информации см. unregisterListener(SensorEventListener) .

Тестирование с помощью эмулятора Android

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

Эмулятор использует соединение с устройством 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 .

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

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

Избегайте использования устаревших методов или типов датчиков.

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

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

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

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

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