Смартфоны полны аппаратных датчиков, таких как акселерометры, датчики света, гироскопы и так далее. Узнайте, как собирать данные с этих датчиков с помощью использования барометрического регистратора.
В продолжение предыдущего урока «Выбор компонентов приложения» на этот раз мы будем реализовывать код для регулярного считывания данных барометрического датчика. Вы узнаете, как читать данные датчика и как планировать повторяющиеся события, чтобы приложение и его сервис не оставались запущенными.
Начиная
В этом руководстве предполагается, что у вас есть базовые знания об Android и Java, что у вас установлены и работают все инструменты Android, и что вы можете загружать и тестировать приложения на устройстве Android. Мы будем использовать аппаратный датчик барометра в этом приложении. Если у вас нет устройства с этим датчиком, вы можете заменить его другим аналогичным датчиком для целей тестирования, но результаты или полезность могут быть не эквивалентны.
Часть A: Чтение датчиков
Доступ к аппаратным датчикам осуществляется через класс SensorManager. Через экземпляр этого класса приложение может запрашивать различные объекты Sensor, которые представляют базовое оборудование. Используя их, приложение может затем начать прослушивать данные, поступающие от сенсорного оборудования.
Шаг 1: Нахождение барометрического датчика
Сначала получите экземпляр класса SensorManager, например так:
1
|
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
|
Затем запросите датчик Sensor.TYPE_PRESSURE, который является барометрическим датчиком:
1
|
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
|
Использование класса SensorManager и считывание датчиков не требует специальных разрешений.
Шаг 2. Регистрация для событий датчика
Теперь вы можете зарегистрироваться, чтобы получать события от датчика. Для этого вы должны реализовать 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, датчик может быть недоступен (по любой причине).
Шаг 3: Чтение событий
Класс для получения событий датчиков является общим для всех датчиков через класс 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
}
|
Шаг 3: Остановка прослушивания датчика
Наконец, чтобы прекратить прослушивание датчика, просто отмените его регистрацию:
1
|
sensorManager.unregisterListener(myListenerInstance);
|
Вы должны зарегистрироваться только тогда, когда ваше приложение готово и нуждается в данных датчика, а затем отменить регистрацию, как только она больше не нужна. Это помогает разумно использовать ресурсы устройства, такие как заряд батареи.
Часть B: Работа в фоновом режиме
Вы, вероятно, не заинтересованы в том, чтобы приложение постоянно запускалось для проверки значений датчика. Вместо этого создайте Службу и регулярно запускайте Службу с помощью повторяющегося сигнала тревоги через AlarmManager.
Шаг 1: Внедрение Сервиса
Служба на 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();
}
}
|
Шаг 2: Запись данных в фоновом режиме
Запись данных на диск является блокирующей операцией и должна выполняться в фоновом потоке. Простой способ сделать это через 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
}
}
|
Шаг 3: Запуск службы
Служба обычно запускается с помощью метода Context startService (). Например, из класса Activity будет работать следующее:
1
2
|
Intent intent = new Intent(getApplicationContext(), BaroLoggerService.class );
startService(intent);
|
Это не то, что мы хотим. Мы хотим, чтобы Служба запускалась только изредка, даже если приложение не запущено и даже если устройство спит. Класс AlarmManager позволяет нам сделать это.
Шаг 4. Планирование повторяющегося обслуживания
Класс 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 часа . Когда они не пишут, они тратят свое время на разработку мобильного программного обеспечения в своей компании и оказание консультационных услуг. С ними можно связаться по электронной почте [email protected] , через их блог на androidbook.blogspot.com и в Twitter @androidwireless .