Vấn đề:
Nhận vị trí hiện tại của người dùng trong ngưỡng ASAP và đồng thời tiết kiệm pin.
Tại sao vấn đề là một vấn đề:
Trước hết, android có hai nhà cung cấp; mạng và GPS. Đôi khi mạng tốt hơn và đôi khi GPS tốt hơn.
"Tốt hơn" tôi có nghĩa là tốc độ so với tỷ lệ chính xác.
Tôi sẵn sàng hy sinh độ chính xác vài mét nếu tôi có thể có được vị trí gần như ngay lập tức và không cần bật GPS.
Thứ hai, nếu bạn yêu cầu cập nhật thay đổi vị trí, không có gì được gửi nếu vị trí hiện tại ổn định.
Google có một ví dụ về việc xác định vị trí "tốt nhất" tại đây: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Nhưng tôi nghĩ rằng không có nơi nào tốt như nó nên /có thể là.
Tôi hơi bối rối tại sao google không có API được chuẩn hóa cho vị trí, nhà phát triển không cần phải quan tâm vị trí đó đến từ đâu, bạn chỉ nên chỉ định những gì bạn muốn và điện thoại sẽ chọn cho bạn.
Những gì tôi cần giúp đỡ với:
Tôi cần tìm một cách tốt để xác định vị trí "tốt nhất", có thể là một số heuristic hoặc có thể thông qua một số thư viện của bên thứ 3.
Điều này không có nghĩa là xác định nhà cung cấp tốt nhất!
Tôi có thể sẽ sử dụng tất cả các nhà cung cấp và chọn những người giỏi nhất trong số họ.
Bối cảnh của ứng dụng:
Ứng dụng sẽ thu thập vị trí của người dùng theo một khoảng thời gian cố định (giả sử cứ sau 10 phút hoặc lâu hơn) và gửi nó đến một máy chủ.
Ứng dụng nên tiết kiệm pin càng nhiều càng tốt và vị trí phải có độ chính xác X (50 - 100?).
Mục tiêu là sau này có thể vẽ đường đi của người dùng trong ngày trên bản đồ để tôi cần đủ độ chính xác cho điều đó.
Linh tinh
Bạn nghĩ gì về giá trị hợp lý trên độ chính xác mong muốn và được chấp nhận?
Tôi đã sử dụng 100m như được chấp nhận và 30m như mong muốn, đây có phải là điều đáng để hỏi không?
Tôi muốn có thể vẽ đường dẫn của người dùng trên bản đồ sau này.
Là 100m cho mong muốn và 500m cho chấp nhận tốt hơn?
Ngoài ra, ngay bây giờ tôi bật GPS tối đa 60 giây cho mỗi lần cập nhật vị trí, điều này có quá ngắn để có được vị trí nếu bạn ở trong nhà với độ chính xác có thể là 200m không?
Đây là mã hiện tại của tôi, mọi phản hồi đều được đánh giá cao (ngoài việc thiếu kiểm tra lỗi là 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);
}