Статьи

Android Barometer Logger: получение данных с датчиков

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

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


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


Доступ к аппаратным датчикам осуществляется через класс SensorManager. Через экземпляр этого класса приложение может запрашивать различные объекты Sensor, которые представляют базовое оборудование. Используя их, приложение может затем начать прослушивать данные, поступающие от сенсорного оборудования.


Сначала получите экземпляр класса SensorManager, например так:

1
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

Затем запросите датчик Sensor.TYPE_PRESSURE, который является барометрическим датчиком:

1
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);

Использование класса SensorManager и считывание датчиков не требует специальных разрешений.


Теперь вы можете зарегистрироваться, чтобы получать события от датчика. Для этого вы должны реализовать SensorEventListener и сообщить об этом классу SensorManager.

Например, вот структура для реализации SensorEventListener:

01
02
03
04
05
06
07
08
09
10
11
class MyListener implements SensorEventListener {
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO
    }
 
    @Override
    public void onSensorChanged(SensorEvent event) {
        // TODO
    }
}

Затем зарегистрируйтесь для событий, рассказав SensorManager о своем слушателе:

1
2
sensorManager.registerListener(myListenerInstance, sensor,
                    SensorManager.SENSOR_DELAY_NORMAL);

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


Класс для получения событий датчиков является общим для всех датчиков через класс SensorEvent. Таким образом, поле значения SensorEvent является массивом для обработки большего количества данных, чем обеспечивает наш единственный числовой барометрический датчик. Нас интересует только первое значение массива и отметка времени:

1
2
3
4
5
6
7
@Override
public void onSensorChanged(SensorEvent event) {
    long timestamp = event.timestamp;
    float value = event.values[0];
 
    // do something with the values
}

Наконец, чтобы прекратить прослушивание датчика, просто отмените его регистрацию:

1
sensorManager.unregisterListener(myListenerInstance);

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


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


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

Ядро класса Service имеет два реализуемых метода: метод onStartCommand () и метод onBind (). Так как мы не обрабатываем связывание, мы будем обрабатывать onBind () просто. Вот ядро ​​нашего Сервиса, включая реализацию SensorEventListener:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class BaroLoggerService extends Service implements SensorEventListener {
    private static final String DEBUG_TAG = «BaroLoggerService»;
 
    private SensorManager sensorManager = null;
    private Sensor sensor = null;
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
        sensorManager.registerListener(this, sensor,
                    SensorManager.SENSOR_DELAY_NORMAL);
 
 
        return START_STICKY;
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
 
 
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // do nothing
    }
 
    @Override
    public void onSensorChanged(SensorEvent event) {
        // grab the values and timestamp
        //…
        // stop the sensor and service
        sensorManager.unregisterListener(this);
        stopSelf();
    }
 
 
}

Запись данных на диск является блокирующей операцией и должна выполняться в фоновом потоке. Простой способ сделать это через AsyncTask. В Сервисе мы можем добавить AsyncTask и передать его SensorEvent для обработки следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Override
public void onSensorChanged(SensorEvent event) {
    // grab the values and timestamp — off the main thread
    new SensorEventLoggerTask().execute(event);
    // stop the service
    stopSelf();
}
 
private class SensorEventLoggerTask extends
        AsyncTask<SensorEvent, Void, Void> {
    @Override
    protected Void doInBackground(SensorEvent… events) {
SensorEvent event = events[0];
        // log the value
    }
}

Служба обычно запускается с помощью метода Context startService (). Например, из класса Activity будет работать следующее:

1
2
Intent intent = new Intent(getApplicationContext(), BaroLoggerService.class );
startService(intent);

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


Класс AlarmManager позволяет планировать события периодически. Он даже позволяет эффективно планировать множество различных событий, а не только ваши собственные, что является хорошим способом снижения расхода батареи.

Вот код, который вы можете поместить в Activity под обработчиком кнопки, например, чтобы включить службу для повторения один раз в час:

1
AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

Мы используем метод setInexactRepeating () с определенным интервалом, INTERVAL_HOUR, чтобы воспользоваться преимуществами системы, которая просыпается не только для нашей работы. Кроме того, мы используем опцию RTC_WAKEUP, чтобы указать, что мы хотим, чтобы устройство проснулось для этой тревоги, а не ждать, пока оно не проснется по какой-то другой причине.

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

Выключите будильник с помощью вызова метода cancel () с той же целью:

1
2
3
4
5
AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this,BaroLoggerService.class );
PendingIntent scheduledIntent = PendingIntent.getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
 
scheduler.cancel(scheduledIntent);

Вы можете выполнять эти звонки любым удобным вам способом. Мы создали пару кнопок управления, которые вызывали методы в нашей Деятельности с этими вызовами.


В этом руководстве вы научились регулярно проверять значение датчика в фоновом режиме, используя объекты SensorManager, Service и AlarmManager. Серия будет продолжена с дополнительной информацией о хранении и отображении данных.

В качестве проблемы, реализовать этот код самостоятельно. Попробуйте использовать другие типы датчиков, которые поддерживает ваше устройство. Поддерживает ли он TYPE_AMBIENT_TEMPERATURE? TYPE_RELATIVE_HUMIDITY? TYPE_LIGHT? Было бы полезно записать местоположение измерений? Будьте изобретательны и расскажите нам, что вы придумали в комментариях!


Разработчики мобильных приложений Лорен Дарси и Шейн Кондер являются соавторами нескольких книг по разработке Android: углубленная книга по программированию под названием « Разработка беспроводных приложений для Android» (в третьем выпуске — в виде двухтомника) и « Sams Teach Yourself» — разработка приложений для Android за 24 часа . Когда они не пишут, они тратят свое время на разработку мобильного программного обеспечения в своей компании и оказание консультационных услуг. С ними можно связаться по электронной почте androidwirelessdev+mt@gmail.com , через их блог на androidbook.blogspot.com и в Twitter @androidwireless .

Купить разработку беспроводных приложений для Android, 3-е издание, том 1 Купить Sam's Teach Yourself для Android разработки приложений в течение 24 часов, 2-е издание Код Мамламбо в Код-Каньоне