Los servicios basados en ubicación abarcan tres aspectos: obtener la ubicación, servicios de mapas y servicios de geocodificación.
Para obtener la ubicación existen dos métodos: mediante GPS, que brinda alta precisión pero consume más batería; o mediante redes WLAN o estaciones base de telefonía, que ofrecen menor precisión pero consumen menos energía. La información de ubicación obtenida es tridimensional e incluye longitud, latitud y altitud.
Los servicios de mapas permiten visualizar puntos de coordenadas en un mapa y convertir puntos del mapa en coordenadas. Esto se logra invocando las API de proveedores de mapas (como Google, Baidu, Amap) para obtener información del mapa desde sus servidores.
Los servicios de geocodificación convierten coordenadas en direcciones y viceversa. Se implementan mediante protocolo HTTP invocando servicios de direcciones en Internet.
Obtener la ubicación es la base de todos los servicios basados en ubicación. En Android se utiliza la clase LocationManager. Primero se obtiene una instancia de LocationManager, luego se verifica si el servicio de ubicación está activo. Si no lo está, se abre la pantalla de configuración del servicio de ubicación. Si está activo, se verifica que la aplicación tenga los permisos de localización necesarios. Una vez aprobado, se configura un listener de ubicación que invocará métodos cuando haya nueva información. Al configurar el listener se puede establecer un intervalo mínimo de tiempo y una distancia mínima; solo cuando se cumplan ambas condiciones se generará una ubicación. El flujo y el código clave se muestran a continuación:
A continuación se presenta un ejemplo de obtención de ubicación. La interfaz del ejemplo tiene en la primera fila dos configuraciones: intervalo mínimo de tiempo y distancia mínima para registrar la ubicación. Solo cuando se cumplen ambas condiciones se genera un dato de ubicación. La segunda fila tiene dos botones: iniciar y detener el listener de ubicación. Debajo hay un texto que muestra la información de ubicación recibida.
En el ejemplo, primero en onCreate se obtiene la instancia de LocationManager y se verifica si el dispositivo tiene activado el servicio de ubicación. Código:
administrador = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (!administrador.isProviderEnabled(LocationManager.GPS_PROVIDER) &&
!administrador.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
preguntarConfiguracionUbicacion();
}
Se puede activar/desactivar el servicio de ubicación desde la barra superior del teléfono, como se muestra en la imagen:
Si el servicio de ubicación no está activo, se puede preguntar al usuario si desea ir a la pantalla de configuración del servicio de ubicación. Esta pantalla se inicia mediante un Intent con la acción android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS. Código:
void preguntarConfiguracionUbicacion() {
AlertDialog.Builder constructor = new AlertDialog.Builder(this);
constructor.setTitle("Activar servicio de ubicación");
constructor.setMessage("Esta aplicación necesita el servicio de ubicación. ¿Desea ir a la configuración para activarlo?");
constructor.setPositiveButton("Sí", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogo, int id) {
Intent intento = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
intento.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.this.startActivity(intento);
}
});
constructor.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogo, int id) {
Toast.makeText(MainActivity.this, "No hay proveedor de ubicación disponible", Toast.LENGTH_SHORT).show();
}
});
constructor.show();
}
La pantalla de configuración del servicio de ubicación se ve así:
Si el dispositivo tiene el servicio de ubicación activo, se puede crear un listener de ubicación para pasarlo al LocationManager. En el método de escucha se extraen longitud, latitud, altitud, velocidad, etc. Código:
escuchador = new LocationListener() {
public void onLocationChanged(Location ubicacion) {
String texto = "Longitud: " + ubicacion.getLongitude()
+ ", Latitud: " + ubicacion.getLatitude()
+ ", Altitud: " + ubicacion.getAltitude()
+ ", Velocidad: " + ubicacion.getSpeed();
tvResultado.append(texto + "\n");
}
public void onProviderDisabled(String proveedor) {}
public void onProviderEnabled(String proveedor) {}
public void onStatusChanged(String proveedor, int estado, Bundle extras) {}
};
Para iniciar la escucha se invoca el método requestLocationUpdates() de LocationManager. El primer parámetro es el proveedor de servicios de ubicación. Un dispositivo puede tener varios proveedores (GPS, WiFi, BeiDou, etc.), por lo que primero se debe encontrar el proveedor más adecuado según los requisitos de precisión usando el método getBestProvider(). Además, obtener la ubicación actual puede tomar tiempo; algunas aplicaciones necesitan una ubicación inmediatamente al iniciar, por lo que se puede usar la última ubicación conocida almacenada en caché mediante getLastKnownLocation(). Código:
void iniciarEscucha() {
Criterio criterio = new Criterio();
criterio.setHorizontalAccuracy(Criterio.ACCURACY_HIGH);
criterio.setAltitudeRequired(true);
String proveedor = administrador.getBestProvider(criterio, true);
Location ultimaUbicacion = administrador.getLastKnownLocation(proveedor);
if (ultimaUbicacion != null) {
tvResultado.append(proveedor + "-Última conocida: " + ultimaUbicacion.toString() + "\n");
}
long intervalo = Long.parseLong(etPeriodo.getText().toString());
int distancia = Integer.parseInt(etDistancia.getText().toString());
administrador.requestLocationUpdates(proveedor, intervalo * 1000, distancia, escuchador);
tvResultado.append(proveedor + "-Listener de ubicación iniciado.\n");
}
En Android 6.0 y superiores se requiere solicitar permisos en tiempo de ejecución. Por lo tanto, antes de iniciar la escucha se debe verificar el permiso y solicitarlo si no está concedido. Código principal:
if (ActivityCompat.checkSelfPermission(actividad, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(actividad, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 101);
return;
}
Para detener la escucha se usa removeUpdates():
administrador.removeUpdates(escuchador);
tvResultado.append("Listener de ubicación detenido.\n");
Es necesario declarar los permisos en el archivo de manifiesto:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Clases relacionadas utilizadas en el ejemplo:
- android.location.LocationManager: métodos:
boolean isProviderEnabled(String provider): indica si el proveedor está habilitado. Valores típicos:LocationManager.GPS_PROVIDERoLocationManager.NETWORK_PROVIDER.String getBestProvider(Criteria criteria, boolean enabledOnly): devuelve el proveedor que mejor se ajusta a los criterios. SienabledOnlyestrue, solo considera proveedores habilitados. Posibles retornos:GPS_PROVIDER,NETWORK_PROVIDER,PASSIVE_PROVIDER(obtiene ubicación de otras apps).Location getLastKnownLocation(String provider): devuleve la última ubicación conocida (almacenada en caché), puede no ser precisa. Retornanullsi el proveedor no está habilitado.void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener): registra la actividad para recibir actualizaciones de ubicación. El listener se invoca cuando se cumplen los mínimos de tiempo (ms) y distancia (m). No usarminTimemenor a 60000 ms para ahorrar batería. Debe llamarse desde el hilo principal.void removeUpdates(LocationListener listener): elimina el listener.void requestLocationUpdates(String provider, long minTime, float minDistance, PendingIntent intent): notifica cambios mediante unPendingIntent(broadcast), puede llamarse desde un hilo secundario.void removeUpdates(PendingIntent intent): elimina la notificación por broadcast.
- android.location.Criteria: métodos:
void setHorizontalAccuracy(int accuracy): valores:ACCURACY_HIGH,ACCURACY_MEDIUM,ACCURACY_LOW.void setAltitudeRequired(boolean required): indica si se requiere altitud.
- android.location.Location: métodos:
double getLongitude()double getLatitude()double getAltitude()float getSpeed()(metros/segundo)
- android.location.LocationListener: interfaz con métodos:
void onLocationChanged(Location location)void onProviderDisabled(String provider)void onProviderEnabled(String provider)void onStatusChanged(String provider, int status, Bundle extras)