Geolocalizzazione con Android

16 marzo 2016

L’utente passa giornate spostandosi e raccogliendo informazioni – più o meno volutamente – nel proprio device Android. Consultazioni internet, appunti, eventi calendario, foto, chiamate, messaggi. Il dispositivo diventa una specie di “diario errante” dell’esperienza di vita quotidiana. La possibilità di associare informazioni geografiche a questi ricordi apre scenari nuovi e ciò basta a giustificare la rapidissima diffusione che ha avuto nell’informatica mobile la geolocalizzazione.

Intendiamo con questo termine la capacità di un dispositivo di rilevare la propria posizione geografica nel mondo reale. Non stiamo parlando ormai di una dote rara, quasi ogni smartphone o tablet oggi contiene dei sistemi di localizzazione.

I più comuni sono:

  • network-based: rileva le reti mobili Wi-Fi e GSM disponibili nella zona ed in base a questi calcola la propria posizione. Non molto accurato ma immancabile nei dispositivi;
  • GPS (Global Positioning System): acronimo famosissimo, si basa sull’intercettazione di messaggi inviati da satelliti che ruotano attorno alla Terra. Tali comunicazioni contengono l’informazione oraria ed altri dati relativi all’orbita percorsa. Il dispositivo intercettando i segnali di almeno quattro di questi satelliti con l’applicazione di formule matematiche riesce a calcolare la propria posizione. Accurato e diffusissimo tranne che in alcuni dispositivi di fascia bassa. Praticamente il sistema di localizzazione per antonomasia.

In questa lezione, viene trattata la localizzazione mediante un componente detto LocationManager. Tali API, nonostante svolgano correttamente il loro lavoro e siano state usate ampiamente dagli sviluppatori, non dovrebbero più essere impiegate nei nuovi progetti e, ove possibile, sostituite in quelli esistenti. L’alternativa consigliata dalla documentazione ufficiale è costituita dai Location Services, opportunamente approfonditi nella Guida ai Servizi Google, disponibile su HTML.it. Essi offrono vantaggi in termini di prestazioni e accuratezza di misurazione, a fronte di una migliore gestione delle risorse energetiche nonchè una naturale integrazione con i servizi di Google.

Esempio pratico: GPS nell’Activity

Entriamo subito nel vivo creando un’Activity che richiede informazioni GPS e le mostra nel suo layout. Oltre a latitudine e longitudine l’Activity mediante un oggetto denominato GeoCoder recupererà l’indirizzo cui corrisponde la posizione.

Il layout dell’activity è una griglia molto semplice. TableLayout con una serie di campi di testo da completare:

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
	android:layout_height="wrap_content">
	<TableRow android:padding="5dp">
		<TextView android:text="Abilitato" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
		<TextView android:id="@+id/enabled" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
	</TableRow>
	<TableRow android:padding="5dp">
		<TextView android:text="Data ora" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
		<TextView android:id="@+id/timestamp" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
	</TableRow>
	<TableRow android:padding="5dp">
		<TextView android:text="Latitudine" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
		<TextView android:id="@+id/latitude" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
	</TableRow>
	<TableRow android:padding="5dp">
		<TextView android:text="Longitudine" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
		<TextView android:id="@+id/longitude" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
	</TableRow>
	<TableRow android:padding="5dp">
		<TextView android:text="Località" android:padding="5dp"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
		<TextView android:id="@+id/where" android:padding="5dp"
		    android:lines="2"
		android:layout_width="wrap_content" android:layout_height="wrap_content"/>
	</TableRow>
</TableLayout>

Da ricordare che per l’accesso ai dati GPS è necessaria un’apposita permission. Nel manifest andremo ad inserire questa riga:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Questa permission va bene sia per usare il GPS sia per la localizzazione network-based. Qualora si volesse usare solo quest’ultima è sufficiente la permission ACCESS_COARSE_LOCATION.

All’interno dell’Activity dovremo per prima cosa registrare un Listener presso il LocationManager e lo faremo nel metodo onResume. Tale istanza sarà annullata in onPause.

public class MainActivity extends Activity
{
	private String providerId = LocationManager.GPS_PROVIDER;
	private Geocoder geo = null;
	private LocationManager locationManager=null;
	private static final int MIN_DIST=20;
	private static final int MIN_PERIOD=30000;
	
	private LocationListener locationListener = new LocationListener() 
	{
		. . . 
		. . .
	
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	@Override
	protected void onResume() 
	{
		super.onResume();
		geo=new Geocoder(this, Locale.getDefault());
		locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
		if (location!=null)
			updateGUI(location);
		if (locationManager!=null && locationManager.isProviderEnabled(providerId))
			updateText(R.id.enabled, "TRUE");
		else
			updateText(R.id.enabled, "FALSE");
		locationManager.requestLocationUpdates(providerId, MIN_PERIOD,MIN_DIST, locationListener);
	}
	
	@Override
	protected void onPause() 
	{
		super.onPause();
		if (locationManager!=null && locationManager.isProviderEnabled(providerId))
			locationManager.removeUpdates(locationListener);
	}
. . .
. . .
}

Notare che, nell’onResume, il metodo requestLocationUpdates effettua la vera registrazione del listener. I parametri che utilizza sono:

  • l’id del provider: la costante stringa che individua il tipo di provider da usare;
  • il minimo intervallo di tempo, in millisecondi, che deve trascorrere tra aggiornamenti della posizione;
  • la minima distanza in metri che deve intercorrere tra due misurazioni;
  • l’oggetto che svolge il ruolo di listener. Lo vedremo subito.

L’oggetto listener registrato viene implementato come classe interna all’Activity:

private LocationListener locationListener = new LocationListener() 
	{
		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) 
		{
			
		}
		@Override
		public void onProviderEnabled(String provider) 
		{
			// attivo GPS su dispositivo
			updateText(R.id.enabled, "TRUE");
		}
		@Override
		public void onProviderDisabled(String provider) 
		{
			// disattivo GPS su dispositivo
			updateText(R.id.enabled, "FALSE");
		}
		@Override
		public void onLocationChanged(Location location) 
		 {
			updateGUI(location);
		 }
	};

I primi tre metodi – onStatusChanged, onProviderEnabled, onProviderDisabled – notificano, rispettivamente, se il provider è disponibile o meno, se è abilitato, se è stato disabilitato.

L’ultimo metodo onLocationChanged è il cuore del listener e viene invocato ogni volta che nuove informazioni di posizione sono state recapitate.

L’oggetto Location contiene tutto ciò che è stato appreso dall’ultima misurazione del posizionamento e viene inviata al metodo updateGUI per riflettere gli aggiornamenti sulla interfaccia utente:

private void updateGUI(Location location) 
	{
		Date timestamp = new Date(location.getTime());
		updateText(R.id.timestamp, timestamp.toString());
		double latitude = location.getLatitude();
		updateText(R.id.latitude, String.valueOf(latitude));
		double longitude = location.getLongitude();
		updateText(R.id.longitude, String.valueOf(longitude));
		new AddressSolver().execute(location);
	}

private void updateText(int id, String text) 
	{
		TextView textView = (TextView) findViewById(id);
		textView.setText(text);
	}

All’interno di updateGUI, oltre al codice di modifica delle TextView, è presente l’invocazione al Geocoder per la conversione delle coordinate in un indirizzo vero e proprio. Il Geocoder viene consultato in maniera asincrona mediante AsyncTask. Nel metodo doInBackground, la Location sarà convertita in una stringa frutto della concatenazione delle informazioni reperite:

private class AddressSolver extends AsyncTask<Location, Void, String>
	{

		@Override
		protected String doInBackground(Location... params) 
		{
			Location pos=params[0];
			double latitude = pos.getLatitude();
			double longitude = pos.getLongitude();
			
			List<Address> addresses = null;
			try 
			{
				addresses = geo.getFromLocation(latitude, longitude, 1);
			} 
			catch (IOException e) 
			{	
				
			}
			if (addresses!=null)
			{
				if (addresses.isEmpty()) 
				  { 
					return null;
				  }
				  else {
				     if (addresses.size() > 0) 
				     {   
				    	 StringBuffer address=new StringBuffer();
				    	 Address tmp=addresses.get(0);
				     	 for (int y=0;y<tmp.getMaxAddressLineIndex();y++)
				     		address.append(tmp.getAddressLine(y)+"\n");
				     	 return address.toString();
				     }
				  }
			}
			return null;
		}
		
		@Override
		protected void onPostExecute(String result) 
		{
			if (result!=null)
				updateText(R.id.where, result);
			else
			      updateText(R.id.where, "N.A.");
	
		}
	}
Tutte le lezioni

1 ... 37 38 39 ... 80

Se vuoi aggiornamenti su Geolocalizzazione con Android inserisci la tua e-mail nel box qui sotto:
Tags:
 
X
Se vuoi aggiornamenti su Geolocalizzazione con Android

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy