El problema:
Obtener la ubicación actual del usuario dentro de un umbral lo antes posible y al mismo tiempo ahorrar batería.
Por qué el problema es un problema:
En primer lugar, Android tiene dos proveedores; red y GPS. Algunas veces la red es mejor y otras veces el GPS es mejor.
Por "mejor" me refiero a la velocidad frente a la relación de precisión.
Estoy dispuesto a sacrificar unos pocos metros de precisión si puedo obtener la ubicación casi al instante y sin encender el GPS.
En segundo lugar, si solicita actualizaciones para cambios de ubicación, no se envía nada si la ubicación actual es estable.
Google tiene un ejemplo de determinación de la "mejor" ubicación aquí: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Pero creo que no es tan bueno como debería /podría ser.
Estoy un poco confundido por qué Google no tiene una API normalizada para la ubicación, el desarrollador no debería tener que preocuparse de dónde es la ubicación, solo debe especificar lo que desea y el teléfono debe elegir por usted.
Lo que necesito ayuda con:
Necesito encontrar una buena manera de determinar la "mejor" ubicación, quizás a través de alguna heurística o tal vez a través de una biblioteca de terceros.
¡Esto no significa determinar el mejor proveedor!
Probablemente voy a usar todos los proveedores y elegir el mejor de ellos.
Antecedentes de la aplicación:
La aplicación recopilará la ubicación del usuario en un intervalo fijo (digamos cada 10 minutos más o menos) y la enviará a un servidor.
La aplicación debe conservar la mayor cantidad de batería posible y la ubicación debe tener una precisión de X (50-100?) Metros.
El objetivo es luego poder trazar la ruta del usuario durante el día en un mapa, por lo que necesito suficiente precisión para eso.
Misceláneo:
¿Cuáles cree que son valores razonables en las precisiones deseadas y aceptadas?
He estado usando 100m según lo aceptado y 30m según lo deseado, ¿es mucho pedir?
Me gustaría poder trazar la ruta del usuario en un mapa más adelante.
¿Son 100m para deseados y 500m para aceptados mejor?
Además, en este momento tengo el GPS encendido durante un máximo de 60 segundos por actualización de ubicación, ¿es demasiado corto para obtener una ubicación si está en interiores con una precisión de quizás 200 m?
Este es mi código actual, cualquier comentario es apreciado (aparte de la falta de verificación de errores que es TODO):
protected void runTask() {
final LocationManager locationManager = (LocationManager) context
.getSystemService(Context.LOCATION_SERVICE);
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER));
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
Looper.prepare();
setLooper(Looper.myLooper());
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateBestLocation(location);
if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
return;
// We're done
Looper l = getLooper();
if (l != null) l.quit();
}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
public void onStatusChanged(String provider, int status,
Bundle extras) {
// TODO Auto-generated method stub
Log.i("LocationCollector", "Fail");
Looper l = getLooper();
if (l != null) l.quit();
}
};
// Register the listener with the Location Manager to receive
// location updates
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
Looper.myLooper());
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1000, 1,
locationListener, Looper.myLooper());
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
Looper l = getLooper();
if (l != null) l.quit();
// Log.i("LocationCollector",
// "Stopping collector due to timeout");
}
}, MAX_POLLING_TIME);
Looper.loop();
t.cancel();
locationManager.removeUpdates(locationListener);
setLooper(null);
}
if (getLocationQuality(bestLocation) != LocationQuality.BAD)
sendUpdate(locationToString(bestLocation));
else Log.w("LocationCollector", "Failed to get a location");
}
private enum LocationQuality {
BAD, ACCEPTED, GOOD;
public String toString() {
if (this == GOOD) return "Good";
else if (this == ACCEPTED) return "Accepted";
else return "Bad";
}
}
private LocationQuality getLocationQuality(Location location) {
if (location == null) return LocationQuality.BAD;
if (!location.hasAccuracy()) return LocationQuality.BAD;
long currentTime = System.currentTimeMillis();
if (currentTime - location.getTime() < MAX_AGE
&& location.getAccuracy() <= GOOD_ACCURACY)
return LocationQuality.GOOD;
if (location.getAccuracy() <= ACCEPTED_ACCURACY)
return LocationQuality.ACCEPTED;
return LocationQuality.BAD;
}
private synchronized void updateBestLocation(Location location) {
bestLocation = getBestLocation(location, bestLocation);
}
// Pretty much an unmodified version of googles example
protected Location getBestLocation(Location location,
Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return location;
}
if (location == null) return currentBestLocation;
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use
// the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return location;
// If the new location is more than two minutes older, it must be
// worse
} else if (isSignificantlyOlder) {
return currentBestLocation;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and
// accuracy
if (isMoreAccurate) {
return location;
} else if (isNewer && !isLessAccurate) {
return location;
} else if (isNewer && !isSignificantlyLessAccurate
&& isFromSameProvider) {
return location;
}
return bestLocation;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
fuente
Respuestas:
Parece que estamos codificando la misma aplicación ;-)
Aquí está mi implementación actual. Todavía estoy en la fase de prueba beta de mi aplicación de carga de GPS, por lo que podría haber muchas mejoras posibles. pero parece funcionar bastante bien hasta ahora.
Editar: aquí está la parte que solicita las actualizaciones periódicas de los proveedores de ubicación:
Cosas para considerar:
no solicite actualizaciones de GPS con demasiada frecuencia, agota la batería. Actualmente uso 30 minutos como predeterminado para mi aplicación.
agregue una marca de "distancia mínima a la última ubicación conocida". sin esto, sus puntos "saltarán" cuando el GPS no esté disponible y la ubicación se triangule desde las torres celulares. o puede verificar si la nueva ubicación está fuera del valor de precisión de la última ubicación conocida.
fuente
Para seleccionar el proveedor de ubicación adecuado para su aplicación, puede usar los objetos Criteria :
Lea la documentación de requestLocationUpdates para obtener más detalles sobre cómo se tienen en cuenta los argumentos:
Más pensamientos
Criteria.ACCURACY_HIGH
criterio debería proporcionarle errores por debajo de los 100 m, lo que no es tan bueno como el GPS, pero satisface sus necesidades.fuente
Criteria
pero ¿qué pasa si la última ubicación de red es increíble? No puedo creer que Google haya dificultado esto a los desarrolladores.Respondiendo los dos primeros puntos :
El GPS siempre le dará una ubicación más precisa, si está habilitado y si no hay paredes gruesas alrededor .
Si la ubicación no cambió, puede llamar a getLastKnownLocation (String) y recuperar la ubicación de inmediato.
Usando un enfoque alternativo :
Puede intentar obtener la identificación de la celda en uso o todas las celdas vecinas
Puede referirse a la ubicación de la celda a través de varias bases de datos abiertas (por ejemplo, http://www.location-api.com/ o http://opencellid.org/ )
La estrategia sería leer la lista de ID de torre al leer la ubicación. Luego, en la siguiente consulta (10 minutos en su aplicación), léalos nuevamente. Si al menos algunas torres son iguales, entonces es seguro de usar
getLastKnownLocation(String)
. Si no lo están, espereonLocationChanged()
. Esto evita la necesidad de una base de datos de terceros para la ubicación. También puedes probar este enfoque .fuente
Esta es mi solución que funciona bastante bien:
fuente
La precisión de la ubicación depende principalmente del proveedor de ubicación utilizado:
Si lo que busca es precisión, entonces el GPS es su única opción.
He leído un artículo muy informativo al respecto aquí .
En cuanto al tiempo de espera del GPS, 60 segundos deberían ser suficientes y, en la mayoría de los casos, incluso demasiado. Creo que 30 segundos está bien y, a veces, incluso menos de 5 segundos ...
si solo necesita una única ubicación, le sugiero que, en su
onLocationChanged
método, una vez que reciba una actualización anule el registro del oyente y evite el uso innecesario del GPS.fuente
Actualmente estoy usando ya que es confiable para obtener la ubicación y calcular la distancia para mi aplicación ... estoy usando esto para mi aplicación de taxi.
use la API de fusión que los desarrolladores de Google han desarrollado con la fusión del sensor GPS, el magnetómetro, el acelerómetro y también el uso de Wifi o la ubicación de la celda para calcular o estimar la ubicación. También es capaz de proporcionar actualizaciones de ubicación también dentro del edificio con precisión. para obtener más información, acceda al enlace https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi
fuente
Busqué en Internet una respuesta actualizada (el año pasado) usando los últimos métodos de extracción de ubicación sugeridos por Google (para usar FusedLocationProviderClient). Finalmente llegué a esto:
https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates
Creé un nuevo proyecto y copié la mayor parte de este código. Auge. Funciona. Y creo que sin ninguna línea en desuso.
Además, el simulador no parece tener una ubicación GPS, que yo sepa. Llegó al extremo de informar esto en el registro: "Todas las configuraciones de ubicación están satisfechas".
Y finalmente, en caso de que quisieras saber (lo hice), NO necesitas una clave de API de Google Maps de la consola de desarrolladores de Google, si todo lo que quieres es la ubicación del GPS.
También es útil su tutorial. Pero quería un tutorial completo / ejemplo de código de una página, y eso. Su tutorial se acumula pero es confuso cuando eres nuevo en esto porque no sabes qué piezas necesitas de páginas anteriores.
https://developer.android.com/training/location/index.html
Y finalmente, recuerda cosas como esta:
No solo tuve que modificar mainActivity.Java. También tuve que modificar Strings.xml, androidmanifest.xml, y el build.gradle correcto. Y también tu activity_Main.xml (pero esa parte fue fácil para mí).
Necesitaba agregar dependencias como esta: implementación 'com.google.android.gms: play-services-location: 11.8.0' y actualizar la configuración de mi SDK de Android Studio para incluir los servicios de Google Play. (configuración del archivo configuración del sistema de apariencia Android SDK SDK Tools verifique los servicios de Google Play).
actualización: el simulador de Android parecía tener una ubicación y eventos de cambio de ubicación (cuando cambié el valor en la configuración del sim). Pero mis mejores y primeros resultados fueron en un dispositivo real. Por lo tanto, probablemente sea más fácil probarlo en dispositivos reales.
fuente
Recientemente refactorizado para obtener la ubicación del código, aprender algunas buenas ideas y finalmente lograr una biblioteca y demostración relativamente perfecta.
La respuesta de @Gryphius es buena
Implementación completa: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java
1.Gracias a las ideas de solución @Gryphius, también comparto el código completo.
2.Cada solicitud para completar la ubicación, es mejor eliminar las actualizaciones, de lo contrario, la barra de estado del teléfono siempre mostrará el ícono de posicionamiento
fuente
En mi experiencia, he encontrado que es mejor ir con la corrección de GPS a menos que no esté disponible. No sé mucho sobre otros proveedores de ubicación, pero sé que para GPS hay algunos trucos que se pueden usar para dar un poco de medida de precisión de gueto. La altitud es a menudo una señal, por lo que puede verificar valores ridículos. Existe la medida de precisión en las correcciones de ubicación de Android. Además, si puede ver la cantidad de satélites utilizados, esto también puede indicar la precisión.
Una forma interesante de tener una mejor idea de la precisión podría ser solicitar un conjunto de soluciones muy rápidamente, como ~ 1 / seg durante 10 segundos y luego dormir durante un minuto o dos. Una charla en la que he estado ha llevado a creer que algunos dispositivos Android lo harán de todos modos. Luego eliminaría los valores atípicos (he escuchado que se menciona el filtro de Kalman aquí) y usaría algún tipo de estrategia de centrado para obtener una solución única.
Obviamente, la profundidad a la que llega depende de cuán difíciles sean sus requisitos. Si tiene un requisito particularmente estricto para obtener LA MEJOR ubicación posible, creo que encontrará que el GPS y la ubicación de la red son tan similares como las manzanas y las naranjas. Además, el GPS puede ser muy diferente de un dispositivo a otro.
fuente
Skyhook (http://www.skyhookwireless.com/) tiene un proveedor de ubicación que es mucho más rápido que el estándar que ofrece Google. Puede ser lo que estás buscando. No estoy afiliado a ellos.
fuente