Содержание статьи
Датчики всякие нужны!
Для работы с аппаратными датчиками, доступными в устройствах под управлением Android, применяется класс SensorManager, ссылку на который можно получить с помощью стандартного метода getSystemService:
SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Чтобы начать работать с датчиком, нужно определить его тип. Удобнее всего это сделать с помощью класса Sensor, так как в нем уже определены все типы сенсоров в виде констант. Рассмотрим их подробнее:
- Sensor.TYPE_ACCELEROMETER — трехосевой акселерометр, возвращающий ускорение по трем осям (в метрах в секунду в квадрате). Связанная система координат представлена на рис. 1.
- Sensor.TYPE_LIGHT — датчик освещенности, возвращающий значение в люксах, обычно используется для динамического изменения яркости экрана. Также для удобства степень освещенности можно получить в виде характеристик — «темно», «облачно», «солнечно» (к этому мы еще вернемся).
- Sensor.TYPE_AMBIENT_TEMPERATURE — термометр, возвращает температуру окружающей среды в градусах Цельсия.
- Sensor.TYPE_PROXIMITY — датчик приближенности, который сигнализирует о расстоянии между устройством и пользователем (в сантиметрах). Когда в момент разговора гаснет экран — срабатывает именно этот датчик. На некоторых девайсах возвращается только два значения: «далеко» и «близко».
- Sensor.TYPE_GYROSCOPE — трехосевой гироскоп, возвращающий скорость вращения устройства по трем осям (радиан в секунду).
- Sensor.TYPE_MAGNETIC_FIELD — магнитометр, определяющий показания магнитного поля в микротеслах (мкТл) по трем осям (имеется в смартфонах с аппаратным компасом).
- Sensor.TYPE_PRESSURE — датчик атмосферного давления (по-простому — барометр), который возвращает текущее атмосферное давление в миллибарах (мбар). Если немного вспомнить физику, то, используя значение этого датчика, можно легко вычислить высоту (а ежели вспоминать ну никак не хочется, можно воспользоваться готовым методом getAltitude из объекта SensorManager).
- Sensor.TYPE_RELATIVE_HUMIDITY — датчик относительной влажности в процентах. Кстати, совместное применение датчиков относительной влажности и давления позволяет предсказывать погоду — конечно, если выйти на улицу. 😉
- Sensor.TYPE_STEP_COUNTER (с API 19) — счетчик шагов с момента включения устройства (обнуляется только после перезагрузки).
- Sensor.TYPE_MOTION_DETECT (с API 24) — детектор движения смартфона. Если устройство находится в движении от пяти до десяти секунд, возвращает единицу (по всей видимости, задел для аппаратной функции «антивор»).
- Sensor.TYPE_HEART_BEAT (с API 24) — детектор биения сердца.
- Sensor.TYPE_HEART_RATE (с API 20) — датчик, возвращающий пульс (ударов в минуту). Этот датчик примечателен тем, что требует явного разрешения android.permission.BODY_SENSORS в манифесте.

Перечисленные датчики являются аппаратными и работают независимо друг от друга, часто без всякой фильтрации или нормализации значений. «Для облегчения жизни разработчиков»™ Google ввела несколько так называемых виртуальных сенсоров, которые предоставляют более упрощенные и точные результаты.
Например, датчик Sensor.TYPE_GRAVITY пропускает показания акселерометра через низкочастотный фильтр и возвращает текущие направление и величину силы тяжести по трем осям, а Sensor.TYPE_LINEAR_ACCELERATION использует уже высокочастотный фильтр и получает показатели ускорения по трем осям (без учета силы тяжести).
При разработке приложения, эксплуатирующего показания сенсоров, вовсе не обязательно бегать по улице или прыгать в воду с высокой скалы, так как эмулятор, входящий в поставку Android SDK, умеет передавать приложению любые отладочные значения (рис. 2–3).
Ищем датчики
Чтобы узнать, какие сенсоры есть в смартфоне, следует использовать метод getSensorList объекта SensorManager:
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
Полученный список будет включать все поддерживаемые датчики: как аппаратные, так и виртуальные (рис. 4). Более того, некоторые из них будут иметь различные независимые реализации, отличающиеся количеством потребляемой энергии, задержкой, рабочим диапазоном и точностью.
Для получения списка всех доступных датчиков конкретного типа необходимо указать соответствующую константу. Например, код
List<Sensor> pressureList = sensorManager.getSensorList(Sensor.TYPE_PRESSURE);
вернет все доступные барометрические датчики. Причем аппаратные реализации окажутся в начале списка, а виртуальные — в конце (правило действует для всех типов датчиков).

Чтобы получить реализацию датчика по умолчанию (такие датчики хорошо подходят для стандартных задач и сбалансированы в плане энергопотребления), используется метод getDefaultSensor:
Sensor defPressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
Если для заданного типа датчика существует аппаратная реализация, по умолчанию будет возвращена именно она. Когда нужного варианта нет, в дело вступает виртуальная версия, ну а если, увы, ничего подходящего в девайсе не окажется, getDefaultSensor вернет null .
О том, как самолично выбирать реализацию датчиков по критериям, написано во врезке, мы же плавно двигаемся дальше.
Снимаем показания
Чтобы получать события, генерируемые датчиком, необходимо зарегистрировать реализацию интерфейса SensorEventListener с помощью того же SensorManager. Звучит сложновато, но на практике реализуется одной строчкой:
Sensor defPressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
sensorManager.registerListener(workingSensorEventListener, defPressureSensor, SensorManager.SENSOR_DELAY_NORMAL);
Здесь мы полученный ранее барометр по умолчанию регистрируем с помощью метода registerListener, передавая в качестве второго параметра сенсор, а в качестве третьего — частоту обновления данных.
В классе SensorManager определены четыре статические константы, определяющие частоту обновления:
- SensorManager.SENSOR_DELAY_FASTEST — максимальная частота обновления данных;
- SensorManager.SENSOR_DELAY_GAME — частота, обычно используемая в играх, поддерживающих гироскоп;
- SensorManager.SENSOR_DELAY_NORMAL — частота обновления по умолчанию;
- SensorManager.SENSOR_DELAY_UI — частота, подходящая для обновления пользовательского интерфейса.
Нужно сказать, что, указывая частоту обновления, не стоит ожидать, что она будет строго соблюдаться. Как показывает практика, данные от сенсора могут приходить как быстрее, так и медленнее.
Оставшийся нерассмотренным первый параметр представляет собой реализацию интерфейса SensorEventListener, где мы наконец-то получим конкретные цифры:
private final SensorEventListener workingSensorEventListener = new SensorEventListener() {
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public void onSensorChanged(SensorEvent event) {
// Получаем атмосферное давление в миллибарах
double pressure = event.values[0];
}
};
В метод onSensorChanged передается объект SensorEvent, описывающий все события, связанные с датчиком: event.sensor — ссылка на датчик, event.accuracy — точность значения датчика (см. ниже), event.timestamp — время возникновения события в наносекундах и, самое главное, массив значений event.values. Для датчика давления передается только один элемент, тогда как, например, для акселерометра предусмотрено сразу три элемента для каждой из осей. В следующих разделах мы рассмотрим примеры работы с различными датчиками.
Метод onAccuracyChanged позволяет отслеживать изменение точности передаваемых значений, определяемой одной из констант: SensorManager.SENSOR_STATUS_ACCURACY_LOW — низкая точность, SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM — средняя точность, возможна калибровка, SensorManager.SENSOR_STATUS_ACCURACY_HIGH — высокая точность, SensorManager.SENSOR_STATUS_UNRELIABLE — данные недостоверны, нужна калибровка.
После того как отпадает необходимость работы с датчиком, следует отменить регистрацию:
sensorManager.unregisterListener(workingSensorEventListener);
Меряем давление и высоту
Весь код для работы с датчиком давления мы уже написали в предыдущем разделе, получив в переменной pressure вполне себе значение атмосферного давления в миллибарах.
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее