Registros del barómetro en Android: Adquisición de datos del sensor
Spanish (Español) translation by Steven (you can also view the original English article)
Los teléfonos inteligentes están llenos de sensores de hardware, como acelerómetros, sensores de luz, giroscopios, etc. Aprende a recopilar datos de estos sensores mediante la implementación de un registrador barométrico.
Como continuación del último tutorial, selección de componentes de la aplicación, esta vez implementaremos código para leer datos de sensores barométricos de forma regular. Aprenderás a leer los datos del sensor y a programar eventos recurrentes para que la aplicación y su servicio no tengan que seguir ejecutándose.
Empezando
Este tutorial asume que tienes un conocimiento básico de Android y Java, que tienes todas las herramientas de Android instaladas y funcionando, y que te sientes cómodo cargando y probando aplicaciones en un dispositivo Android. Usaremos el sensor de hardware del barómetro en esta aplicación. Si no tienes un dispositivo con este sensor, puedes sustituirlo por otro sensor similar para realizar pruebas, pero los resultados o la utilidad pueden no ser equivalentes.
Parte A: Lectura de sensores
Se accede a los sensores de hardware a través de la clase SensorManager. A través de una instancia de esta clase, una aplicación puede consultar varios objetos Sensor que representan el hardware subyacente. Con estos, una aplicación puede comenzar a escuchar datos provenientes del hardware del sensor.
Paso 1: Encontrar el sensor barométrico
Primero, obtén una instancia de la clase SensorManager, como esta:
1 |
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); |
A continuación, consulta el sensor Sensor.TYPE_PRESSURE, que es el sensor barométrico:
1 |
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); |
El uso de la clase SensorManager y los sensores de lectura no requiere ningún permiso especial.
Paso 2: Registro para eventos de sensor
Ahora puedes registrarte para recibir eventos del sensor. Para hacer esto, debes implementar un SensorEventListener e informar a la clase SensorManager al respecto.
Por ejemplo, aquí está la estructura para una implementación de SensorEventListener:
1 |
class MyListener implements SensorEventListener { |
2 |
@Override
|
3 |
public void onAccuracyChanged(Sensor sensor, int accuracy) { |
4 |
// TODO
|
5 |
}
|
6 |
|
7 |
@Override
|
8 |
public void onSensorChanged(SensorEvent event) { |
9 |
// TODO
|
10 |
}
|
11 |
}
|
Luego, regístrate para eventos informándole al SensorManager sobre tu oyente:
1 |
sensorManager.registerListener(myListenerInstance, sensor, |
2 |
SensorManager.SENSOR_DELAY_NORMAL); |
Puedes registrarte para recibir actualizaciones de eventos a diferentes ritmos. Cuanto más rápida sea la velocidad, más frecuentes serán las actualizaciones, pero también más rápido se agotará la batería del dispositivo. Aquí usamos el ritmo predeterminado. Verifica el valor de retorno de la llamada registerListener() para ver si fue exitosa o no. Si es verdadero, el sensor está disponible y la escucha ha comenzado. Si es falso, es posible que el sensor no esté disponible (por cualquier motivo).
Paso 3: Leer eventos
La clase para recibir eventos de sensor es genérica para todos los sensores a través de la clase SensorEvent. Como tal, el campo de valor SensorEvent es una matriz para manejar más datos de los que proporciona nuestro único sensor barométrico numérico. Solo nos interesa el primer valor de la matriz y la marca de tiempo:
1 |
@Override
|
2 |
public void onSensorChanged(SensorEvent event) { |
3 |
long timestamp = event.timestamp; |
4 |
float value = event.values[0]; |
5 |
|
6 |
// do something with the values
|
7 |
}
|
Paso 3: Detener la escucha del sensor
Finalmente, para dejar de escuchar el sensor, simplemente cancela el registro:
1 |
sensorManager.unregisterListener(myListenerInstance); |
Debes registrarte solo cuando tu aplicación esté lista y necesite los datos del sensor, y luego anular el registro tan pronto como ya no los necesites. Esto ayuda a utilizar de forma inteligente los recursos del dispositivo, como la energía de la batería.
Parte B: Ejecución en segundo plano
Probablemente no estés interesado en dejar la aplicación ejecutándose todo el tiempo para verificar los valores del sensor. En su lugar, crea un servicio y haz que el servicio se inicie con regularidad mediante el uso de una alarma repetida a través de AlarmManager.
Paso 1: Implementación del servicio
Un servicio en Android es una forma de ejecutar el código de la aplicación que no está vinculado a una actividad en particular y no necesita una interfaz de usuario propia. Al igual que una actividad, el código de un servicio se ejecuta en el subproceso principal, por lo que se deben realizar técnicas normales de procesamiento en segundo plano para operaciones largas. Un Servicio se puede utilizar a través de un Intent o vinculando y realizando llamadas a métodos remotos. El tipo de servicio que estamos implementando se realiza mejor a través del manejo de intenciones.
El núcleo de una clase de servicio tiene dos métodos para implementar, el método onStartCommand() y el método onBind(). Dado que no estamos manejando el enlace, manejaremos onBind() simplemente. Aquí está el núcleo de nuestro servicio, incluida la implementación de SensorEventListener:
1 |
public class BaroLoggerService extends Service implements SensorEventListener { |
2 |
private static final String DEBUG_TAG = "BaroLoggerService"; |
3 |
|
4 |
private SensorManager sensorManager = null; |
5 |
private Sensor sensor = null; |
6 |
|
7 |
@Override
|
8 |
public int onStartCommand(Intent intent, int flags, int startId) { |
9 |
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); |
10 |
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); |
11 |
sensorManager.registerListener(this, sensor, |
12 |
SensorManager.SENSOR_DELAY_NORMAL); |
13 |
|
14 |
|
15 |
return START_STICKY; |
16 |
}
|
17 |
|
18 |
@Override
|
19 |
public IBinder onBind(Intent intent) { |
20 |
return null; |
21 |
}
|
22 |
|
23 |
|
24 |
@Override
|
25 |
public void onAccuracyChanged(Sensor sensor, int accuracy) { |
26 |
// do nothing
|
27 |
}
|
28 |
|
29 |
@Override
|
30 |
public void onSensorChanged(SensorEvent event) { |
31 |
// grab the values and timestamp
|
32 |
//...
|
33 |
// stop the sensor and service
|
34 |
sensorManager.unregisterListener(this); |
35 |
stopSelf(); |
36 |
}
|
37 |
|
38 |
|
39 |
}
|
Paso 2: Registrar los datos en segundo plano
Escribir datos en el disco es una operación de bloqueo y debe realizarse en un hilo en segundo plano. Una forma sencilla de hacer esto es a través de AsyncTask. Dentro del Servicio, podemos agregar una AsyncTask y pasarle el SensorEvent para su manejo de la siguiente manera:
1 |
@Override
|
2 |
public void onSensorChanged(SensorEvent event) { |
3 |
// grab the values and timestamp -- off the main thread
|
4 |
new SensorEventLoggerTask().execute(event); |
5 |
// stop the service
|
6 |
stopSelf(); |
7 |
}
|
8 |
|
9 |
private class SensorEventLoggerTask extends |
10 |
AsyncTask<SensorEvent, Void, Void> { |
11 |
@Override
|
12 |
protected Void doInBackground(SensorEvent... events) { |
13 |
SensorEvent event = events[0]; |
14 |
// log the value
|
15 |
}
|
16 |
}
|
Paso 3: Iniciar un servicio
Normalmente, un servicio se inicia mediante el método de contexto startService(). Por ejemplo, lo siguiente funcionaría desde una clase de actividad:
1 |
Intent intent = new Intent(getApplicationContext(), BaroLoggerService.class ); |
2 |
startService(intent); |
Sin embargo, esto no es lo que queremos. Queremos que el Servicio se ejecute solo ocasionalmente, incluso si la aplicación no se está ejecutando, e incluso si el dispositivo está inactivo. La clase AlarmManager nos permite hacer esto.
Paso 4: Programar un servicio recurrente
La clase AlarmManager te permite programar eventos de forma recurrente. Incluso permite la programación eficiente de muchos eventos diferentes, no solo los tuyos, lo cual es una buena manera de reducir el uso de la batería.
Aquí está el código, que puedes colocar en una Actividad debajo de un controlador de botón, por ejemplo, para activar el Servicio y repetirlo una vez por hora:
1 |
AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE); |
2 |
Intent intent = new Intent(getApplicationContext(), BaroLoggerService.class ); |
3 |
PendingIntent scheduledIntent = PendingIntent.getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); |
4 |
|
5 |
scheduler.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), AlarmManager.INTERVAL_HOUR, scheduledIntent); |
Usamos el método setInexactRepeating() con un intervalo específicamente definido, INTERVAL_HOUR, para aprovechar el despertar del sistema para algo más que nuestro trabajo. Además, usamos la opción RTC_WAKEUP para indicar que queremos que el dispositivo sea despertado por esta alarma, en lugar de esperar hasta que se despierte por alguna otra razón.
Además, con fines de prueba, utilizamos un intervalo de un minuto para no tener que esperar varias horas para recopilar varios puntos de datos.
Apaga la alarma con una llamada al método cancel() con la misma intención:
1 |
AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE); |
2 |
Intent intent = new Intent(this,BaroLoggerService.class ); |
3 |
PendingIntent scheduledIntent = PendingIntent.getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); |
4 |
|
5 |
scheduler.cancel(scheduledIntent); |
Puedes realizar estas llamadas de la forma que desees. Creamos un par de controles de botón que llamaban métodos en nuestra Actividad con estas llamadas.
Conclusión
En este tutorial, has aprendido a verificar regularmente un valor de sensor en segundo plano utilizando los objetos SensorManager, Service y AlarmManager. La serie continuará con más información sobre cómo almacenar y mostrar los datos.
Como desafío, implementa este código por tu cuenta. Prueba otros tipos de sensores compatibles con tu dispositivo. ¿Es compatible con TYPE_AMBIENT_TEMPERATURE o TYPE_RELATIVE_HUMIDITY o TYPE_LIGHT? ¿Sería útil registrar la ubicación de las mediciones? ¡Sé creativo y cuéntanos en los comentarios qué se te ocurrió!
Acerca de los autores
Los desarrolladores de dispositivos móviles Lauren Darcey y Shane Conder han sido coautores de varios libros sobre desarrollo de Android: un libro de programación en profundidad titulado Desarrollo de aplicaciones inalámbricas de Android (ahora en su tercera edición como un conjunto de dos volúmenes) y Sams enséñate a ti mismo el desarrollo de aplicaciones para Android en 24 horas. Cuando no están escribiendo, pasan su tiempo desarrollando software móvil en su empresa y brindando servicios de consultoría. Puedes comunicarte con ellos por correo electrónico a androidwirelessdev+mt@gmail.com, a través de su blog en androidbook.blogspot.com y en Twitter @androidwireless.



