Masalah:
Mendapatkan lokasi pengguna saat ini dalam ambang ASAP dan pada saat yang sama menghemat baterai.
Mengapa masalahnya adalah masalah:
Pertama, Android memiliki dua penyedia; jaringan dan GPS. Terkadang jaringan lebih baik dan terkadang GPS lebih baik.
Dengan "lebih baik" Maksudku rasio kecepatan vs akurasi.
Saya bersedia mengorbankan akurasi beberapa meter jika saya bisa mendapatkan lokasi hampir instan dan tanpa menyalakan GPS.
Kedua, jika Anda meminta pembaruan untuk perubahan lokasi tidak dikirim jika lokasi saat ini stabil.
Google memiliki contoh untuk menentukan lokasi "terbaik" di sini: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Tapi saya pikir tidak ada yang sedekat yang seharusnya. /bisa jadi.
Saya agak bingung mengapa google belum memiliki API yang dinormalisasi untuk lokasi, pengembang tidak perlu peduli dari mana lokasi itu berasal, Anda hanya perlu menentukan apa yang Anda inginkan dan telepon harus memilih untuk Anda.
Apa yang saya perlu bantuan:
Saya perlu menemukan cara yang baik untuk menentukan lokasi "terbaik", mungkin melalui beberapa heuristik atau mungkin melalui beberapa perpustakaan pihak ke-3.
Ini tidak berarti menentukan penyedia terbaik!
Saya mungkin akan menggunakan semua penyedia dan memilih yang terbaik dari mereka.
Latar belakang aplikasi:
Aplikasi ini akan mengumpulkan lokasi pengguna pada interval yang tetap (misalkan setiap 10 menit atau lebih) dan mengirimkannya ke server.
Aplikasi ini harus menghemat baterai sebanyak mungkin dan lokasi harus memiliki akurasi X (50-100?) Meter.
Tujuannya adalah untuk nantinya dapat memplot jalur pengguna pada siang hari di peta jadi saya perlu akurasi yang cukup untuk itu.
Lain-lain:
Menurut Anda apa nilai yang masuk akal pada akurasi yang diinginkan dan diterima?
Saya telah menggunakan 100m sebagai diterima dan 30m seperti yang diinginkan, apakah ini banyak bertanya?
Saya ingin dapat memplot jalur pengguna di peta nanti.
Apakah 100m untuk yang diinginkan dan 500m untuk diterima lebih baik?
Juga, saat ini saya memiliki GPS aktif selama maksimum 60 detik per pembaruan lokasi, apakah ini terlalu pendek untuk mendapatkan lokasi jika Anda berada di dalam ruangan dengan akurasi mungkin 200m?
Ini adalah kode saya saat ini, ada umpan balik yang dihargai (terlepas dari kurangnya pengecekan kesalahan yaitu 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);
}