Android e la gestione dei sensori

di  Antonio Coschignano, lunedì 24 maggio 2010
I dispositivi mobili di ultima generazione meglio conosciuti come smartphone sono dotati di diversi tipi di sensori. Un sensore è un particolare dispositivo che trasforma una grandezza fisica - come il campo magnetico, la pressione o la forza di gravità - in un segnale di altra natura che in questo contesto diventa un'informazione digitale.

Come sappiamo, Android gira solo ed esclusivamente su questi tipi di dispositivi che sono tecnologicamente molto avanzati. Quindi ovviamente la piattaforma è predisposta - attraverso un set di librerie - all'utilizzo di questi componenti integrati. La cosa sorprendente è la facilità con cui ci permette di accedere ai sensori e manipolari i dati, oppure verificare quali tipi di sensori possiede un determinato dispositivo.

Sensor e SensorManager

La classe che si occupa della gestione dei sensori è android.hardware.SensorManager di cui si può ottenere un'istanza attraverso il metodo getSystemService() della classe Context (superclasse dell'Activity). Il metodo riceve come parametro un intero che rappresenta appunto l'identificativo del tipo di servizio a cui vogliamo accedere. Nel nostro caso l'identificativo è rappresentato dalla costante statica Context.SENSOR_SERVICE. Vediamo come ottenere un riferimento del SensorManager all'interno di un'Activity:
SensorManager manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Un sensore invece è rappresentato da una particolare istanza della classe android.hardware.Sensor. La classe Sensor implementa i seguenti metodi:
  • public float getMaximumRange():portata massima del sensore
  • public String getName():il nome del sensore
  • public float getPower():la potenza in mA utilizzati da questo sensore durante l'uso
  • public float getResolution():risoluzione del sensore:
  • public int getType():ritorna un intero che identifica la tipologia di sensori a cui è associato.
  • public int getVersion(): versione del modulo sensore
Per accedere al sensore, cioè ottenere un'istanza di un tipo di sensore dobbiamo utilizzare il SensorManager che per tale scopo implementa questo metodo:
public List<Sensor> getSensorList(int n)
L'intero che riceve il metodo come parametro identifica il tipo di sensore, definite nella classe Sensor da alcune costanti statiche. Vediamole:
  1. TYPE_ACCELEROMETER: sensore accelerometro
  2. TYPE_GYROSCOPE: sensore giroscopio
  3. TYPE_LIGHT: sensore per la luce
  4. TYPE_MAGNETIC_FIELD: sensore magnetico
  5. TYPE_ORIENTATION: deprecata.Si deve utilizzare invece il metodo SensorManager.getOrientation()
  6. TYPE_PRESSURE: sensore di pressione
  7. TYPE_PROXIMITY: sensore di prossimità
  8. TYPE_TEMPERATURE: sensore di temperatura
  9. TYPE_ALL: tutti i tipi di sensori
E' ovvio che il metodo getSensorList() ritorna una lista di sensori in base alla tipologia poichè possono essere presenti su un dispositivo più sensori dello stesso tipo. Per ottenere ad esempio la lista di tutti i sensori presenti in un dispositivo utilizziamo la costante Sensor.TYPE_ALL:
List<Sensor> list = manager.getSensorList(Sensor.TYPE_ALL);

SensorEventListener

Adesso bisogna accedere ai dati che i sensori rilevano. L'approcio è asincrono, cioè ci registriamo attraverso un listener ed in base ha un delay impostato da noi ci vengono notificati i valori che il sensore rileva. Per prima cosa dobbiamo implementare il listener opportuno che viene rappresentato dall'interfaccia SensorEventListener che definisce i seguenti metodi:
  • void onAccuracyChanged(Sensor sensor, int accurancy): viene invocato quando la precisione del sensore è cambiata
  • void onSensorChanged(SensorEvent event): viene invocato quando i valori del sensore sono cambiati
Passiamo adesso all'implementazione del listener. Basta creare una classe ed implementare l'interfaccia SensorEventListener per poi passare alla registrazione per essere notificati su ogni evento. Vediamo una semplice implementazione:
class MySensorListener implements SensorEventListener {
	public void onAccuracyChanged(Sensor sensor, int accurancy) {
		...
	}
	public void onSensorChanged(SensorEvent event) {

		...
	}
}
I valori generati dal sensore vengono incapsulati nella classe SensorEvent che non contiene metodi ma semplicemente membri ad accesso public:
  • public int accuracy: valore che indica l'accuratezza del segnale
  • public Sensor sensor: riferimento all'oggetto Sensor che ha generato l'evento
  • public long timestamp: il timestamp in nanosecondi
  • public final float[] values: i valori veri e propri del segnale acquisito dal sensore. Ad esempio con un accelerometro questi valori sono da intendersi in m/s^2
Ad esempio se riprendiamo il listener che abbiamo implementato - in particolare il metodo onSensorChanged() che è quello più importante - possiamo accedere ai membri in questo modo:
public void onSensorChanged(SensorEvent event) {
	float [] f = event.values;
	for(int i = 0; i < f.length; i++)
		Log.i("Valore "+i+" :",""+f[i]);//Metodo simile al System.out.println, che in questo caso
		                                //genera un log nella finestra LogCat dell'ADT
}

Registrare il listener

Per registrare il listener si usa il SensorManager che implementa i metodi sia per la regsitrazione che per la disinstallazione del listener. Ad esempio immagianiamo di avere un accelerometro installato sul nostro dispositivo, vediamo come rilevarlo ed installare il listener:
List<Sensor> list = manager.getSensorList(Sensor.TYPE_ACCELEROMETER);
Sensor s = list.get(0);
manager.registerListener(new MySensorListener(), s, SensorManager.SENSOR_DELAY_NORMAL);
La costante SensorManager.SENSOR_DELAY_NORMAL indica la frequenza con cui ci vengono aggiornati i dati e questo rappresenta un livello normale. Abbiamo altri livelli di frequenza come SensorManager.SENSOR_DELAY_FAST che indica una frequenza altissima, mentre SensorManager.SENSOR_DELAY_UI e SensorManager.SENSOR_DELAY_GAME adtte per l'interfaccia grafica e per i giochi.

Esempio di un'applicazione con i sensori

Per concludere vediamo un esempio di un'applicazione che rileva tutti i tipi di sensori che sono presenti nel dispositivo. Per ogni sensore rilevato installa un listener e visualizza sul display (attraverso delle TextView) il nome ed i valori rilevati da ciascun sensore. Per quanto riguarda l'interfaccia grafica in questo caso è stata implementata senza ricorrere al layout definito in XML ma semplicemnte creando l'interfaccia grafica direttamente da codice. Quindi create un nuovo progetto e definite il nome dell'activity TestSensor, poi basta copiare ed incollare il codice di questa classe all'interno dell'activity:
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TableLayout;
import android.widget.TextView;

public class TestSensor extends Activity {

  private TableLayout layout;
  private SensorManager manager;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    layout = new TableLayout(this);
    setContentView(layout);
    manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    List<Sensor> list = manager.getSensorList(Sensor.TYPE_ALL);
    for (Sensor s : list) {
      TextView[] v = installBoxSensor(s);
      installListenerSensor(s, v);
    }
  }

  private TextView[] installBoxSensor(Sensor s) {
    TextView view = new TextView(this);
    view.setText(s.getName());
    layout.addView(view);
    TextView[] values = new TextView[3];
    for (int i = 0; i < values.length; i++) {
      values[i] = new TextView(this);
      layout.addView(values[i]);
    }
    return values;
  }

  //Installazione del listener
  private void installListenerSensor(Sensor s, TextView[] view) {
    manager.registerListener(new MySensorListener(view), s,
    SensorManager.SENSOR_DELAY_NORMAL);
  }

  //Implementazione del listener
  private class MySensorListener implements SensorEventListener {

    private TextView value[];

    public MySensorListener(TextView value[]) {
      this.value = value;
    }

    @Override
    public void onAccuracyChanged(Sensor arg0, int arg1) {}

    @Override
    public void onSensorChanged(SensorEvent arg0) {
      float[] values = arg0.values;
      value[0].setText("X: " + values[SensorManager.DATA_X]);
      value[1].setText("Y: " + values[SensorManager.DATA_Y]);
      value[2].setText("Z: " + values[SensorManager.DATA_Z]);
    }
  }
}
L'immagine qui sotto è lo screenshot dell'applicazione in esecuzione su un'HTC Magic:

Esempio di TestSensor

La maggior parte dei sensori sono di tipo triassali cioè distribuiscono la grandezza fisica sui tre assi X, Y e Z. Ma ad esempio il sensore della temperatura notifica un solo valore, quindi gli altri valori sono nulli.

Le applicazioni che si possono sviluppare attraverso l'utilizzo dei sensori sono molteplici. Vanno dai giochi come ad esempio Labyrinth Lite oppure strumenti di utilità come la bussola o il famoso Metal Detector che fanno uso del sensore magnetico. Quindi tutto ciò naturalmente implica una certa conoscenza di nozioni matematiche e fisiche per poter elaborare in maniera corretta questi dati.
Altri link che potrebbero interessarti
  • » Struttura di un'applicazione Android
  • » Screenshot su un dispositivo Android
  • » Pubblicare applicazioni nell'Android Market
  • » Integrare Google Analytics nelle applicazioni Android
  • » Integrare AdMob nelle applicazioni Android
  • » Guida all'installazione dell'Android SDK
  • » Guida agli Adapter e le ListView in Android
  • » Gestire Thread e GUI nelle applicazioni Android
  • » Applicazioni Android ed il supporto multilingue
  • » Android VideoView e le Youtube Api
  • » Android Linkify e i collegamenti testuali
  • » Android HTML TextView
  • » Android e tecniche multithreading
  • » Android e l'interfaccia Parcelable

  • 2 Commenti per "Android e la gestione dei sensori"

    Autore: moment

    Ottimo articolo, complimenti e grazie per la condivisione! probabilmente la riga: TableLayout layout = new TableLayout(this); dichiarata nell'onCreate dovrebbe essere: layout = new TableLayout(this); per non creare una nuova istanza (che rimarrebbe visibile solo nell'oncreate) ed utilizzare quella dichiarata a livello di classe,altrimenti la funzione installBoxSensor() genera null pointer exception

    mercoledì 20 aprile 2011 ore 22:20

    Autore: Antonio Coschignano

    Grazie Moment, per la segnalazione. Ho corretto la riga..

    lunedì 30 maggio 2011 ore 17:40

    Lascia un commento

    Nome :
    E-mail :
    Commento :

    Tutti i commenti inseriti devono essere approvati da un amministratore prima di essere visualizzati al pubblico. Si tratta di una misura preventiva contro spam e pubblicità e non è necessario reinviare il commento. Si prega di scrivere commenti in tema. Spam e messaggi promozionali non vengono approvati.