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);
}