Le problème:
Obtenir l'emplacement actuel de l'utilisateur dans un seuil ASAP et en même temps économiser la batterie.
Pourquoi le problème est un problème:
Tout d'abord, Android a deux fournisseurs; réseau et GPS. Parfois, le réseau est meilleur et parfois le GPS est meilleur.
Par "mieux", j'entends le rapport vitesse / précision.
Je suis prêt à sacrifier quelques mètres de précision si je peux obtenir l'emplacement presque instantanément et sans allumer le GPS.
Deuxièmement, si vous demandez des mises à jour pour des changements d'emplacement, rien n'est envoyé si l'emplacement actuel est stable.
Google a un exemple de détermination du "meilleur" emplacement ici: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Mais je pense que ce n'est pas aussi bon qu'il le devrait /pourrait être.
Je suis un peu confus pourquoi Google n'a pas d'API normalisée pour la localisation, le développeur ne devrait pas avoir à se soucier de la provenance de l'emplacement, vous devez simplement spécifier ce que vous voulez et le téléphone doit choisir pour vous.
Avec quoi j'ai besoin d'aide:
J'ai besoin de trouver un bon moyen de déterminer le "meilleur" emplacement, peut-être par le biais d'une heuristique ou par le biais d'une bibliothèque tierce.
Cela ne veut pas dire déterminer le meilleur fournisseur!
Je vais probablement utiliser tous les fournisseurs et choisir les meilleurs d'entre eux.
Contexte de l'application:
L'application collectera l'emplacement de l'utilisateur à un intervalle fixe (disons toutes les 10 minutes environ) et l'enverra à un serveur.
L'application doit économiser autant de batterie que possible et l'emplacement doit avoir une précision de X (50-100?) Mètres.
L'objectif est de pouvoir plus tard tracer le chemin de l'utilisateur pendant la journée sur une carte, j'ai donc besoin d'une précision suffisante pour cela.
Divers:
Selon vous, quelles sont les valeurs raisonnables des précisions souhaitées et acceptées?
J'utilise 100m comme accepté et 30m comme souhaité, est-ce trop demander?
J'aimerais pouvoir tracer le chemin de l'utilisateur sur une carte plus tard.
Est-ce que 100m pour mieux et 500m pour mieux accepté?
De plus, en ce moment, j'ai le GPS allumé pendant un maximum de 60 secondes par mise à jour de position, est-ce trop court pour obtenir une position si vous êtes à l'intérieur avec une précision de peut-être 200 m?
Ceci est mon code actuel, tout commentaire est apprécié (à part le manque de vérification d'erreur qui est 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);
}
la source
Réponses:
On dirait que nous codons la même application ;-)
Voici mon implémentation actuelle. Je suis encore dans la phase de test bêta de mon application de téléchargement GPS, il pourrait donc y avoir de nombreuses améliorations possibles. mais cela semble assez bien fonctionner jusqu'à présent.
Modifier: voici la partie qui demande les mises à jour périodiques aux fournisseurs de localisation:
Choses à considérer:
ne demandez pas les mises à jour GPS trop souvent, cela décharge la batterie. J'utilise actuellement 30 min par défaut pour mon application.
ajouter une vérification «distance minimale au dernier emplacement connu». sans cela, vos points «sauteront» lorsque le GPS n'est pas disponible et que l'emplacement est triangulé à partir des tours de téléphonie cellulaire. ou vous pouvez vérifier si le nouvel emplacement est en dehors de la valeur de précision du dernier emplacement connu.
la source
Pour sélectionner le bon fournisseur de localisation pour votre application, vous pouvez utiliser des objets Critères :
Lisez la documentation de requestLocationUpdates pour plus de détails sur la façon dont les arguments sont pris en compte:
Plus de pensées
Criteria.ACCURACY_HIGH
critère devrait vous donner des erreurs en dessous de 100m, ce qui n'est pas aussi bon que le GPS peut l'être, mais correspond à vos besoins.la source
Criteria
mais que se passe-t-il si le dernier emplacement réseau est génial (il peut le savoir via le wifi) et qu'il ne faut pas de temps ou de batterie pour l'obtenir (getLastKnown), alors les critères ne tiendront probablement pas compte de cela et retourneront le GPS à la place. Je ne peux pas croire que Google ait rendu cela difficile pour les développeurs.Répondre aux deux premiers points :
Le GPS vous donnera toujours un emplacement plus précis, s'il est activé et s'il n'y a pas de murs épais autour .
Si l'emplacement n'a pas changé, vous pouvez appeler getLastKnownLocation (String) et récupérer immédiatement l'emplacement.
En utilisant une approche alternative :
Vous pouvez essayer d'obtenir l' ID de cellule en cours d'utilisation ou toutes les cellules voisines
Vous pouvez ensuite vous référer à l'emplacement des cellules via plusieurs bases de données ouvertes (par exemple, http://www.location-api.com/ ou http://opencellid.org/ )
La stratégie serait de lire la liste des identifiants des tours lors de la lecture de l'emplacement. Ensuite, dans la requête suivante (10 minutes dans votre application), relisez-les. Si au moins certaines tours sont identiques, il est sûr de les utiliser
getLastKnownLocation(String)
. Si ce n'est pas le cas, attendezonLocationChanged()
. Cela évite le besoin d'une base de données tierce pour l'emplacement. Vous pouvez également essayer cette approche .la source
Voici ma solution qui fonctionne assez bien:
la source
La précision de la localisation dépend principalement du fournisseur de localisation utilisé:
Si c'est la précision que vous recherchez, le GPS est votre seule option.
J'ai lu un article très instructif à ce sujet ici .
En ce qui concerne le délai d'attente GPS - 60 secondes devraient être suffisantes, et même trop dans la plupart des cas. Je pense que 30 secondes est OK et parfois même moins de 5 secondes ...
si vous n'avez besoin que d'un seul emplacement, je suggère que dans votre
onLocationChanged
méthode, une fois que vous aurez reçu une mise à jour, vous désenregistrerez l'auditeur et éviterez une utilisation inutile du GPS.la source
Actuellement, j'utilise car c'est fiable pour obtenir l'emplacement et calculer la distance pour mon application ...... j'utilise ceci pour mon application de taxi.
utilisez l'API de fusion que le développeur Google a développée avec la fusion du capteur GPS, du magnétomètre et de l'accéléromètre en utilisant également le Wifi ou la localisation des cellules pour calculer ou estimer la position. Il est également capable de fournir des mises à jour de localisation également à l'intérieur du bâtiment avec précision. pour plus de détails, accédez au lien https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi
la source
J'ai parcouru Internet pour trouver une réponse mise à jour (l'année dernière) en utilisant les dernières méthodes d'extraction de position suggérées par Google (pour utiliser FusedLocationProviderClient). J'ai finalement atterri sur ceci:
https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates
J'ai créé un nouveau projet et copié dans la plupart de ce code. Boom. Ça marche. Et je pense sans aucune ligne obsolète.
De plus, le simulateur ne semble pas obtenir de position GPS, à ma connaissance. Il est allé jusqu'à signaler cela dans le journal: "Tous les paramètres de localisation sont satisfaits."
Et enfin, au cas où vous voudriez savoir (je l'ai fait), vous N'AVEZ PAS besoin d'une clé api google maps depuis la console développeur de google, si tout ce que vous voulez c'est la position GPS.
Leur tutoriel est également utile. Mais je voulais un tutoriel / exemple de code complet d'une page, et cela. Leur didacticiel s'empile mais est déroutant lorsque vous êtes nouveau dans ce domaine car vous ne savez pas de quels éléments vous avez besoin dans les pages précédentes.
https://developer.android.com/training/location/index.html
Et enfin, rappelez-vous des choses comme ceci:
J'ai non seulement dû modifier le mainActivity.Java. J'ai également dû modifier Strings.xml, androidmanifest.xml ET le build.gradle correct. Et aussi votre activity_Main.xml (mais cette partie a été facile pour moi).
J'ai dû ajouter des dépendances comme celle-ci: implémentation 'com.google.android.gms: play-services-location: 11.8.0', et mettre à jour les paramètres de mon SDK de studio Android pour inclure les services google play. (paramètres du fichier apparence paramètres système android SDK SDK Tools vérifier les services google play).
mise à jour: le simulateur Android semble avoir obtenu un emplacement et des événements de changement de lieu (lorsque j'ai changé la valeur dans les paramètres de la sim). Mais mes meilleurs et premiers résultats étaient sur un appareil réel. Il est donc probablement plus facile de tester sur des appareils réels.
la source
Récemment refactorisé pour obtenir l'emplacement du code, apprendre quelques bonnes idées, et enfin réalisé une bibliothèque et une démo relativement parfaites.
@ La réponse de Gryphius est bonne
Implémentation complète: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java
1.Merci aux idées de solution @Gryphius, je partage également le code complet.
2.Chaque demande pour terminer l'emplacement, il est préférable de supprimer les mises à jour, sinon la barre d'état du téléphone affichera toujours l'icône de positionnement
la source
D'après mon expérience, j'ai trouvé préférable d'utiliser le correctif GPS, sauf s'il n'est pas disponible. Je ne connais pas beaucoup les autres fournisseurs de localisation, mais je sais que pour le GPS, il existe quelques astuces qui peuvent être utilisées pour donner une mesure de précision du ghetto. L'altitude est souvent un signe, vous pouvez donc vérifier les valeurs ridicules. Il y a la mesure de précision sur les correctifs de localisation Android. De plus, si vous pouvez voir le nombre de satellites utilisés, cela peut également indiquer la précision.
Une façon intéressante d'avoir une meilleure idée de la précision pourrait être de demander un ensemble de correctifs très rapidement, comme ~ 1 / sec pendant 10 secondes, puis de dormir pendant une minute ou deux. Une conversation que j'ai eue a conduit à croire que certains appareils Android le feront de toute façon. Vous élimineriez ensuite les valeurs aberrantes (j'ai entendu le filtre de Kalman mentionné ici) et utiliser une sorte de stratégie de centrage pour obtenir un seul correctif.
Évidemment, la profondeur que vous atteignez ici dépend de la dureté de vos besoins. Si vous avez une exigence particulièrement stricte pour obtenir LE MEILLEUR emplacement possible, je pense que vous constaterez que le GPS et l'emplacement du réseau sont aussi similaires que les pommes et les oranges. Le GPS peut également être très différent d'un appareil à l'autre.
la source
Skyhook (http://www.skyhookwireless.com/) dispose d'un fournisseur de localisation bien plus rapide que celui proposé par Google. C'est peut-être ce que vous recherchez. Je ne suis pas affilié avec eux.
la source