문제 :
최대한 빨리 임계 값 내에 사용자의 현재 위치를 확보하고 배터리를 절약합니다.
문제가 문제인 이유 :
우선, 안드로이드에는 두 개의 제공자가 있습니다. 네트워크 및 GPS. 때로는 네트워크가 더 좋고 때로는 GPS가 더 좋습니다.
"더 나은"이란 속도 대 정확도 비율을 의미합니다.
GPS를 켜지 않고도 위치를 거의 즉각적으로 확보 할 수 있다면 몇 미터의 정확도를 기꺼이 희생하려고합니다.
둘째, 위치 변경에 대한 업데이트를 요청하면 현재 위치가 안정적인 경우 아무것도 전송되지 않습니다.
구글은 여기에 "최고"의 위치를 결정하는 예를 가지고 : http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
하지만 예상대로이 좋은대로 아무 곳 근처 생각하지 /있을 수 있습니다.
Google이 위치에 대해 표준화 된 API를 사용하지 않은 이유는 혼란 스럽습니다. 개발자가 위치를 신경 쓰지 않아도됩니다. 원하는 것을 지정하고 전화를 선택해야합니다.
내가 도움이 필요한 것 :
휴리스틱이나 타사 라이브러리를 통해 "최상의"위치를 결정하는 좋은 방법을 찾아야합니다.
이것은 최고의 공급자를 결정한다는 의미는 아닙니다!
아마 모든 제공자를 사용하고 최선을 다할 것입니다.
앱의 배경 :
앱은 고정 된 간격으로 (10 분 정도 말하자) 사용자의 위치를 수집하여 서버로 보냅니다.
앱은 가능한 한 많은 배터리를 보존해야하며 위치는 X (50-100?) 미터 정확도를 가져야합니다.
목표는 나중에 낮에 사용자의 경로를지도에 표시하여 충분한 정확도가 필요하다는 것입니다.
기타 :
원하는 정확도와 허용 정확도에 대한 합리적인 가치는 무엇이라고 생각하십니까?
나는 100m을 허용하고 30m을 원하는대로 사용하고 있습니다.
나중에지도에서 사용자의 경로를 그릴 수 있기를 원합니다.
원하는 경우 100m, 허용되는 경우 500m가 더 좋습니까?
또한 현재 위치 업데이트 당 최대 60 초 동안 GPS를 켰습니다. 200m 정도의 정확도로 실내에있을 경우 위치를 찾기에는 너무 짧습니까?
이것은 현재 코드이며, 피드백은 감사합니다 (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);
}