Das Problem:
Erhalten Sie den aktuellen Standort des Benutzers so schnell wie möglich innerhalb eines Schwellenwerts und schonen Sie gleichzeitig die Batterie.
Warum das Problem ein Problem ist:
Zunächst einmal hat Android zwei Anbieter; Netzwerk und GPS. Manchmal ist das Netzwerk besser und manchmal ist das GPS besser.
Mit "besser" meine ich das Verhältnis von Geschwindigkeit zu Genauigkeit.
Ich bin bereit, ein paar Meter Genauigkeit zu opfern, wenn ich den Standort fast sofort und ohne Einschalten des GPS ermitteln kann.
Zweitens wird nichts gesendet, wenn Sie Aktualisierungen für Standortänderungen anfordern, wenn der aktuelle Standort stabil ist.
Google hat hier ein Beispiel für die Ermittlung des "besten" Standorts: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Aber ich denke, es ist nicht annähernd so gut wie es sollte /könnte sein.
Ich bin etwas verwirrt, warum Google keine normalisierte API für den Standort hat. Der Entwickler sollte sich nicht darum kümmern müssen, woher der Standort stammt. Sie sollten nur angeben, was Sie möchten, und das Telefon sollte für Sie auswählen.
Womit ich Hilfe brauche:
Ich muss einen guten Weg finden, um den "besten" Ort zu bestimmen, vielleicht durch eine Heuristik oder durch eine Bibliothek eines Drittanbieters.
Dies bedeutet nicht, den besten Anbieter zu ermitteln!
Ich werde wahrscheinlich alle Anbieter nutzen und die besten auswählen.
Hintergrund der App:
Die App sammelt den Standort des Benutzers in einem festgelegten Intervall (etwa alle 10 Minuten) und sendet ihn an einen Server.
Die App sollte so viel Batterie wie möglich sparen und der Standort sollte eine Genauigkeit von X (50-100?) Meter haben.
Das Ziel ist es, später den Pfad des Benutzers während des Tages auf einer Karte darstellen zu können, daher benötige ich dafür eine ausreichende Genauigkeit.
Sonstiges:
Was sind Ihrer Meinung nach vernünftige Werte für gewünschte und akzeptierte Genauigkeiten?
Ich habe 100 m wie akzeptiert und 30 m wie gewünscht verwendet. Ist das zu viel verlangt?
Ich möchte den Pfad des Benutzers später auf einer Karte darstellen können.
Ist 100m für gewünscht und 500m für akzeptiert besser?
Im Moment habe ich das GPS für maximal 60 Sekunden pro Standortaktualisierung eingeschaltet. Ist dies zu kurz, um einen Standort zu ermitteln, wenn Sie sich in Innenräumen mit einer Genauigkeit von etwa 200 m befinden?
Dies ist mein aktueller Code. Jedes Feedback wird geschätzt (abgesehen von der fehlenden Fehlerprüfung, die TODO ist):
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);
}
quelle
Antworten:
Sieht so aus, als würden wir dieselbe Anwendung codieren ;-)
Hier ist meine aktuelle Implementierung. Ich bin noch in der Beta-Testphase meiner GPS-Uploader-App, daher gibt es möglicherweise viele mögliche Verbesserungen. aber es scheint bisher ziemlich gut zu funktionieren.
Bearbeiten: Hier ist der Teil, der die regelmäßigen Aktualisierungen von den Standortanbietern anfordert:
Dinge, die man beachten muss:
Fordern Sie nicht zu oft GPS-Updates an, da der Akku entladen wird. Ich verwende derzeit 30 Minuten als Standard für meine Anwendung.
Fügen Sie eine Überprüfung der Mindestentfernung zum letzten bekannten Standort hinzu. Ohne dies "springen" Ihre Punkte herum, wenn kein GPS verfügbar ist und der Standort von den Mobilfunkmasten aus trianguliert wird. oder Sie können überprüfen, ob der neue Standort außerhalb des Genauigkeitswerts des letzten bekannten Standorts liegt.
quelle
Um den richtigen Standortanbieter für Ihre App auszuwählen, können Sie Kriterienobjekte verwenden :
Weitere Informationen zur Berücksichtigung der Argumente finden Sie in der Dokumentation zu requestLocationUpdates :
Weitere Gedanken
Criteria.ACCURACY_HIGH
Kriterium sollte Ihnen Fehler unter 100 m geben, was nicht so gut ist wie GPS, aber Ihren Anforderungen entspricht.quelle
Criteria
aber was ist, wenn der neueste Netzwerkstandort fantastisch ist (er weiß möglicherweise über WLAN Bescheid) und es keine Zeit oder Batterie braucht, um ihn zu erhalten (getLastKnown), dann werden die Kriterien dies wahrscheinlich ignorieren und stattdessen das GPS zurückgeben. Ich kann nicht glauben, dass Google es Entwicklern so schwer gemacht hat.Beantwortung der ersten beiden Punkte :
GPS gibt Ihnen immer einen genaueren Standort, wenn es aktiviert ist und keine dicken Wände vorhanden sind .
Wenn sich der Speicherort nicht geändert hat, können Sie getLastKnownLocation (String) aufrufen und den Speicherort sofort abrufen.
Verwenden eines alternativen Ansatzes :
Sie können versuchen, die Zellen-ID oder alle benachbarten Zellen zu verwenden
Sie können dann über mehrere offene Datenbanken (z. B. http://www.location-api.com/ oder http://opencellid.org/ ) auf den Zellenstandort verweisen.
Die Strategie wäre, die Liste der Turm-IDs beim Lesen des Standorts zu lesen. Lesen Sie sie dann in der nächsten Abfrage (10 Minuten in Ihrer App) erneut. Wenn mindestens einige Türme gleich sind, ist die Verwendung sicher
getLastKnownLocation(String)
. Wenn nicht, warten SieonLocationChanged()
. Dadurch wird die Notwendigkeit einer Datenbank eines Drittanbieters für den Standort vermieden. Sie können diesen Ansatz auch ausprobieren .quelle
Dies ist meine Lösung, die ziemlich gut funktioniert:
quelle
Die Standortgenauigkeit hängt hauptsächlich vom verwendeten Standortanbieter ab:
Wenn Sie nach Genauigkeit suchen, ist GPS Ihre einzige Option.
Ich habe einen sehr informativen Artikel darüber lesen Sie hier .
Für das GPS-Timeout sollten 60 Sekunden ausreichen und in den meisten Fällen sogar zu viel. Ich denke 30 Sekunden sind OK und manchmal sogar weniger als 5 Sekunden ...
Wenn Sie nur einen einzigen Standort benötigen, würde ich vorschlagen, dass Sie bei Ihrer
onLocationChanged
Methode nach Erhalt eines Updates die Registrierung des Listeners aufheben und eine unnötige Verwendung des GPS vermeiden.quelle
Derzeit verwende ich, da dies vertrauenswürdig ist, um den Standort zu ermitteln und die Entfernung für meine Anwendung zu berechnen. Ich verwende dies für meine Taxi-Anwendung.
Verwenden Sie die Fusions-API, die Google-Entwickler mit der Fusion von GPS-Sensor, Magnetometer und Beschleunigungsmesser entwickelt haben. Verwenden Sie außerdem WLAN oder den Standort der Zelle, um den Standort zu berechnen oder zu schätzen. Es ist auch in der Lage, Standortaktualisierungen auch innerhalb des Gebäudes genau zu geben. Weitere Informationen finden Sie unter https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi
quelle
Ich habe das Internet nach einer aktualisierten Antwort (im letzten Jahr) durchsucht und dabei die neuesten von Google vorgeschlagenen Methoden zum Abrufen von Standorten verwendet (um FusedLocationProviderClient zu verwenden). Ich bin endlich darauf gelandet:
https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates
Ich habe ein neues Projekt erstellt und den größten Teil dieses Codes kopiert. Boom. Es klappt. Und ich denke ohne veraltete Zeilen.
Außerdem scheint der Simulator keinen GPS-Standort zu erhalten, von dem ich weiß. Es ging so weit, dies im Protokoll zu melden: "Alle Standorteinstellungen sind erfüllt."
Und schließlich, falls Sie es wissen wollten (ich tat es), benötigen Sie KEINEN Google Maps-API-Schlüssel von der Google-Entwicklerkonsole, wenn Sie nur den GPS-Standort wünschen.
Ebenfalls nützlich ist ihr Tutorial. Aber ich wollte ein vollständiges einseitiges Tutorial / Codebeispiel, und das. Ihr Tutorial stapelt sich, ist aber verwirrend, wenn Sie neu in diesem Bereich sind, da Sie nicht wissen, welche Teile Sie von früheren Seiten benötigen.
https://developer.android.com/training/location/index.html
Und schließlich erinnern Sie sich an solche Dinge:
Ich musste nicht nur die mainActivity.Java ändern. Ich musste auch Strings.xml, androidmanifest.xml UND das richtige build.gradle ändern. Und auch Ihre activity_Main.xml (aber dieser Teil war einfach für mich).
Ich musste Abhängigkeiten wie diese hinzufügen: Implementierung 'com.google.android.gms: play-services-location: 11.8.0' und die Einstellungen meines Android Studio SDK so aktualisieren, dass sie Google Play Services enthalten. (Dateieinstellungen Aussehen Systemeinstellungen Android SDK SDK Tools überprüfen Google Play Services).
Update: Der Android-Simulator schien einen Standort und Standortänderungsereignisse zu erhalten (als ich den Wert in den Einstellungen der Sim änderte). Aber meine besten und ersten Ergebnisse wurden auf einem tatsächlichen Gerät erzielt. Daher ist es wahrscheinlich am einfachsten, auf tatsächlichen Geräten zu testen.
quelle
Kürzlich überarbeitet, um den Speicherort des Codes zu ermitteln, einige gute Ideen zu erlernen und schließlich eine relativ perfekte Bibliothek und Demo zu erhalten.
@ Gryphius 'Antwort ist gut
Vollständige Implementierung: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java
1. Dank der @ Grephius-Lösungsideen teile ich auch den vollständigen Code.
2.Jede Anforderung, den Standort zu vervollständigen, ist am besten, Aktualisierungen zu entfernen, da sonst in der Statusleiste des Telefons immer das Positionierungssymbol angezeigt wird
quelle
Nach meiner Erfahrung ist es am besten, mit dem GPS-Fix zu arbeiten, es sei denn, er ist nicht verfügbar. Ich weiß nicht viel über andere Standortanbieter, aber ich weiß, dass es für GPS einige Tricks gibt, mit denen man ein bisschen Ghetto-Präzision messen kann. Die Höhe ist oft ein Zeichen, so dass Sie nach lächerlichen Werten suchen können. Es gibt das Genauigkeitsmaß für Android-Standortkorrekturen. Auch wenn Sie die Anzahl der verwendeten Satelliten sehen können, kann dies auch die Genauigkeit anzeigen.
Ein interessanter Weg, um eine bessere Vorstellung von der Genauigkeit zu bekommen, könnte darin bestehen, sehr schnell nach einer Reihe von Korrekturen zu fragen, z. B. ~ 1 / s für 10 Sekunden, und dann ein oder zwei Minuten zu schlafen. Ein Vortrag, an dem ich teilgenommen habe, hat zu der Annahme geführt, dass einige Android-Geräte dies sowieso tun werden. Sie würden dann die Ausreißer aussortieren (ich habe gehört, dass der Kalman-Filter hier erwähnt wird) und eine Art Zentrierungsstrategie verwenden, um eine einzelne Korrektur zu erhalten.
Offensichtlich hängt die Tiefe, die Sie hier erreichen, davon ab, wie hoch Ihre Anforderungen sind. Wenn Sie besonders strenge Anforderungen haben, um den bestmöglichen Standort zu erhalten, werden Sie feststellen, dass GPS und Netzwerkstandort so ähnlich sind wie Äpfel und Orangen. Auch GPS kann von Gerät zu Gerät sehr unterschiedlich sein.
quelle
Skyhook (http://www.skyhookwireless.com/) hat einen Standortanbieter, der viel schneller ist als der von Google bereitgestellte Standardanbieter. Es könnte das sein, wonach Sie suchen. Ich bin nicht mit ihnen verbunden.
quelle