Ubicación del usuario en Android
Obtener la ubicación del usuario en Android es un poco menos sencillo que en iOS. Para comenzar la confusión, hay dos formas totalmente diferentes de hacerlo. El primero usa las API de Android android.location.LocationListener
y el segundo usa las API de los servicios de Google Play com.google.android.gms.location.LocationListener
. Veamos los dos.
API de ubicación de Android
Las API de ubicación de Android utilizan tres proveedores diferentes para obtener la ubicación:
LocationManager.GPS_PROVIDER
- Este proveedor determina la ubicación mediante satélites. Dependiendo de las condiciones, este proveedor puede tardar un tiempo en devolver una corrección de ubicación.
LocationManager.NETWORK_PROVIDER
- Este proveedor determina la ubicación según la disponibilidad de la torre celular y los puntos de acceso WiFi. Los resultados se recuperan mediante una búsqueda en la red.
LocationManager.PASSIVE_PROVIDER
- Este proveedor devolverá ubicaciones generadas por otros proveedores. Recibirá actualizaciones de ubicación pasivamente cuando otras aplicaciones o servicios las soliciten sin solicitar las ubicaciones usted mismo.
La esencia de esto es que obtienes un objeto del LocationManager
sistema, implementas LocationListener
y llamas requestLocationUpdates
al LocationManager
.
Aquí hay un fragmento de código:
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location provider.
makeUseOfNewLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
Guía de API de Google sobre estrategias de ubicaciónexplica el código bastante bien. Pero también mencionan que en la mayoría de los casos, obtendrá un mejor rendimiento de la batería, así como una precisión más adecuada, utilizando en su lugar la API de Google Location Services . ¡Ahora comienza la confusión!
- API de servicios de ubicación de Google
La API de servicios de ubicación de Google forma parte del APK de servicios de Google Play ( aquí se explica cómo configurarlo ). Están construidos sobre la API de Android. Estas API proporcionan un "Proveedor de ubicación fusionada" en lugar de los proveedores mencionados anteriormente. Este proveedor elige automáticamente qué proveedor subyacente usar, en función de la precisión, el uso de la batería, etc. Es rápido porque obtiene la ubicación de un servicio de todo el sistema que lo actualiza constantemente. Y puede usar funciones más avanzadas como geofencing.
Para usar los Servicios de ubicación de Google, su aplicación debe conectarse a GooglePlayServicesClient
. Para conectarse con el cliente, su actividad (o fragmento, más o menos) necesita implementarse GooglePlayServicesClient.ConnectionCallbacks
e GooglePlayServicesClient.OnConnectionFailedListener
interfaces. Aquí hay un código de muestra:
public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener {
LocationClient locationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
locationClient = new LocationClient(this, this, this);
}
@Override
public void onConnected(Bundle bundle) {
Location location = locationClient.getLastLocation() ;
Toast.makeText(this, "Connected to Google Play Services", Toast.LENGTH_SHORT).show();
}
@Override
public void onDisconnected() {
Toast.makeText(this, "Connected from Google Play Services.", Toast.LENGTH_SHORT).show();
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// code to handle failed connection
// this code can be found here — http://developer.android.com/training/location/retrieve-current.html
}
- ¿Por qué es
locationClient.getLastLocation()
nulo?
El locationClient.getLastLocation()
tiene la última ubicación conocida del cliente. Sin embargo, el proveedor de ubicación fusionada solo mantendrá la ubicación en segundo plano si al menos un cliente está conectado a ella. Una vez que se conecta el primer cliente, inmediatamente intentará obtener una ubicación. Si su actividad es el primer cliente en conectarse y usted llama de getLastLocation()
inmediato onConnected()
, puede que no sea suficiente tiempo para que ingrese la primera ubicación. Esto resultará en location
ser null
.
Para resolver este problema, debe esperar (indeterminadamente) hasta que el proveedor obtenga la ubicación y luego llamar getLastLocation()
, lo que es imposible saber. Otra opción (mejor) es implementar la com.google.android.gms.location.LocationListener
interfaz para recibir actualizaciones periódicas de ubicación (y apagarla una vez que obtenga la primera actualización).
public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
// . . . . . . . . more stuff here
LocationRequest locationRequest;
LocationClient locationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
// . . . . other initialization code
locationClient = new LocationClient(this, this, this);
locationRequest = new LocationRequest();
// Use high accuracy
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
locationRequest.setInterval(UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
locationRequest.setFastestInterval(FASTEST_INTERVAL);
}
// . . . . . . . . other methods
@Override
public void onConnected(Bundle bundle) {
Location location = locationClient.getLastLocation();
if (location == null)
locationClient.requestLocationUpdates(locationRequest, this);
else
Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
}
// . . . . . . . . other methods
@Override
public void onLocationChanged(Location location) {
locationClient.removeLocationUpdates(this);
// Use the location here!!!
}
En este código, verifica si el cliente ya tiene la última ubicación (en onConnected
). Si no, está solicitando actualizaciones de ubicación y desactivando las solicitudes (en onLocationChanged()
devolución de llamada) tan pronto como reciba una actualización.
Tenga en cuenta que locationClient.requestLocationUpdates(locationRequest, this);
debe estar dentro de la onConnected
devolución de llamada, de lo contrario, recibirá un IllegalStateException
mensaje porque intentará solicitar ubicaciones sin estar conectado al Cliente de servicios de Google Play.
- El usuario ha deshabilitado los Servicios de ubicación
Muchas veces, el usuario deshabilitaría los servicios de ubicación (para ahorrar batería o razones de privacidad). En tal caso, el código anterior aún solicitará actualizaciones de ubicación, pero onLocationChanged
nunca se le llamará. Puede detener las solicitudes comprobando si el usuario ha deshabilitado los servicios de ubicación.
Si su aplicación requiere que habiliten los servicios de ubicación, querrá mostrar un mensaje o un brindis. Desafortunadamente, no hay forma de verificar si el usuario ha deshabilitado los servicios de ubicación en la API de servicios de ubicación de Google. Para esto, tendrá que recurrir a la API de Android.
En su onCreate
método:
LocationManager manager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && !manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
locationEnabled = false;
Toast.makeText(getActivity(), "Enable location services for accurate data", Toast.LENGTH_SHORT).show();
}
else locationEnabled = true;
Y usa la locationEnabled
bandera en tu onConnected
método así:
if (location != null) {
Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
}
else if (location == null && locationEnabled) {
locationClient.requestLocationUpdates(locationRequest, this);
}
ACTUALIZAR
El documento se actualiza, se elimina LocationClient y la API admite habilitar el GPS con un clic desde el cuadro de diálogo:
task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
}
});
task.addOnFailureListener(this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ResolvableApiException) {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
ResolvableApiException resolvable = (ResolvableApiException) e;
resolvable.startResolutionForResult(MainActivity.this,
REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException sendEx) {
// Ignore the error.
}
}
}
});
Enlace https://developer.android.com/training/location/change-location-settings#prompt
Nuevo cliente de ubicación: FusedLocationProviderClient
private FusedLocationProviderClient fusedLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
}
Se recomienda pasar por https://developer.android.com/training/location antes de realizar cualquier tarea de ubicación.