Molto spesso giocando, usando applicazioni o semplicemente tenendo in mano un device ci è capitato di notare che il dispositivo “si accorge” di una serie
di fattori ed eventi fisici: se lo giriamo, se lo scuotiamo e via dicendo.
Altre volte si può essere rimasti stupiti notando che alcune app forniscono informazioni sull'ambiente in cui ci troviamo. Percepiscono magari temperatura,
umidità, luminosità.
“Ma come fa a saperlo?”: la domanda nasce spontanea.
Questo capitolo vuole mostrare che queste funzionalità non celano magie ma un po' di elettronica amalgamata con software ben fatto.
I dispositivi Android grazie ai sensori di cui sono forniti riescono a percepire movimenti, condizioni ambientali e lo faranno sempre più e sempre con
maggiore precisione grazie all'ampliamento costante di queste tecnologie. A noi spetta la parte, se vogliamo, più divertente di tutto questo: leggere
facilmente queste informazioni con le API del framework e usarle per arricchire le nostre app.
Classificazione dei sensori
Iniziamo con un po' di classificazioni.
Innanzitutto, i sensori possono essere suddivisi in tre grandi gruppi:
- sensori di movimento: percepiscono le forze fisiche che agiscono sul dispositivo. Ad esempio, l'accelerometro, il giroscopio, sensore di
gravità; - sensori ambientali: rilevano particolari dell'ambiente in cui ci si trova: temperatura, pressione, umidità;
- sensori di posizione: raccolgono dati sulla posizione del dispositivo, ad esempio il sensore di orientamento.
Inoltre, i sensori, dipendentemente dal modo in cui sono implementati, possono essere hardware o software. I primi
corrispondono a dei veri e propri elementi elettronici inseriti nel dispositivo. I secondi sono delle elaborazioni basate sui dati raccolti dai sensori
hardware. Quelli software sono chiamati anche virtuali in quanto possono essere consultati con lo stesso interfacciamento di quelli hardware dissimulando
quindi la loro natura software.
Alcuni sensori devono essere necessariamente hardware, altri esistono solo software mentre alcuni possono essere hardware o software a seconda
dell'implementazione che è stata scelta per il particolare dispositivo.
Il SensorManager
Come in molte altre situazioni, il sottosistema Android che vogliamo sfruttare ci viene dischiuso da un system service, accessibile mediante una
classe “manager”.
In questo caso, si tratta del SensorManager.
Per ottenerne un riferimento, procediamo ad un passo ormai di rito:
private SensorManager mSensorManager;
. . .
. . .
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
La prima cosa che può essere utile fare con il SensorManager è chiedergli un inventario dei sensori disponibili nel nostro dispositivo.
Usando una serie di costanti intere (tutte ben spiegate nella documentazione ufficiale) si può chiedere una lista dei sensori:
<Sensor> sensors=mSensorManager.getSensorList(Sensor.TYPE_ALL);
oppure verificare se un sensore è disponibile:
Sensor ss=mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
Un po' tutti i dispositivi avranno a disposizione almeno tre o quattro sensori essenziali per la vita di uno smartphone tra cui accelerometro, orientamento
e rotazione.
Leggere dati da un sensore
La prassi comune per ricevere dati periodici da un sensore è registrare un listener nella nostra applicazione. Ciò, da un punto di vista
sintattico, obbligherà all'implementazione di un metodo di callback all'interno del quale si potrà fare un qualche uso delle misurazioni rilevate.
Un tipico schema di Activity che legge dati da un sensore potrebbe essere questo:
public class MainActivity extends Activity implements SensorEventListener
{
private SensorManager mSensorManager;
private Sensor sensor;
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
sensor = mSensorManager.getDefaultSensor(
/*
* Costante relativa al sensore da monitorare
* */
);
}
@Override
public void onSensorChanged(SensorEvent event)
{
/*
* Codice di gestione dei nuovi eventi del sensore
* */
}
@Override
public void onAccuracyChanged(Sensor s, int i)
{
}
}
Gli aspetti da notare maggiormente sono:
- nel metodo
onCreate
è stato prelevato un riferimento al SensorManager. Opzionalmente questo punto sarà buono per recuperare un riferimento anche al
sensore specifico con cui si vuole interagire; - nei metodi
onPause
eonResume
che come sappiamo regolano l'inizio e la fine dell'interazione tra Activity e utente avviene, rispettivamente, la
registrazione e la cancellazione del listener; - l'activity implementa l'interfaccia SensorEventListener che forza all'override di due metodi
onAccuracyChanged
eonSensorChanged
.
Il metodo onSensorChanged costituisce il cuore dell'interazione con il sensore. È qui che arrivano le chiamate del listener ogni volta
che sono disponibili nuove misurazioni. L'evento notificato verrà formalizzato con un oggetto di classe SensorEvent
.
SensorEvent permette di leggere i valori recuparati come un array numerico. Il tutto visto in questo modo potrebbe sembrare semplice. La difficoltà sta
proprio nell'interpretare e sfruttare i valori dell'evento. Essendo i sensori dei misuratori di grandezza fisiche, i dati letti con essi dovrebbero essere
sottoposti ad opportune valutazioni nel rispetto, eventualmente, di leggi scientifiche. In un capitolo successivo, si avrà modo di sperimentare
l'accelerometro ed in quel caso dovremo interpretare delle accelerazioni.