存在的问题:
尽快在阈值范围内获取用户的当前位置,同时节省电池。
为什么这个问题是问题:
首先,android有两个提供者;网络和GPS。有时网络更好,有时GPS更好。
这里的“更好”指的是速度与准确度之比。
我愿意牺牲几米的精度,只要我能在不打开GPS的情况下立即得到位置。
其次,如果您请求更新位置更改,如果当前位置稳定,则不会发送任何内容。
谷歌有一个确定“最佳”位置的例子:http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
但是我认为它远不如它应该/可以做到的那么好。
我有点困惑为什么谷歌没有一个规范化的API的位置,开发人员不应该关心位置从哪里来,你应该指定你想要什么,手机应该为你选择。
我需要帮助的方面:
我需要找到一种确定“最佳”位置的好方法,可能是通过一些启发式或第三方库。
这并不意味着要确定最好的提供者!
我可能会使用所有的提供者,并从中选择最好的。
app背景:
该应用程序将在固定的时间间隔(比如每10分钟左右)收集用户的位置,并将其发送到服务器。
应用程序应该尽可能节省电池,位置应该有X(50-100?)米的精度。
我们的目标是以后能够在地图上绘制用户白天的路径,所以我需要足够的准确性。
Misc:
你认为期望和接受的精确度的合理值是多少?
我一直在用接受的100万和想要的30万,这要求太高了吗?
我希望以后能够在地图上绘制用户的路径。
要求100米,接受500米更好吗?
另外,现在我的GPS每次更新位置最多60秒,如果你在室内,精度可能是200米,这是不是太短了?
这是我目前的代码,任何反馈都是赞赏的(除了缺乏错误检查,这是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);
}
要为你的应用程序选择正确的位置提供者,你可以使用Criteria对象:
Criteria myCriteria = new Criteria();
myCriteria.setAccuracy(Criteria.ACCURACY_HIGH);
myCriteria.setPowerRequirement(Criteria.POWER_LOW);
// let Android select the right location provider for you
String myProvider = locationManager.getBestProvider(myCriteria, true);
// finally require updates at -at least- the desired rate
long minTimeMillis = 600000; // 600,000 milliseconds make 10 minutes
locationManager.requestLocationUpdates(myProvider,minTimeMillis,0,locationListener);
阅读requestLocationUpdates的文档,了解如何考虑参数的更多细节:
通知的频率可以使用minTime和
minDistance参数。如果minTime大于0,则LocationManager . properties将自动返回
可能会在位置更新之间休息minTime毫秒
为了节约能源。如果minDistance大于0,一个位置只会
如果设备按minDistance米移动,则广播。获得
尽可能频繁地通知,将两个参数都设置为0。
更多的想法
You can monitor the accuracy of the Location objects with Location.getAccuracy(), which returns the estimated accuracy of the position in meters.
the Criteria.ACCURACY_HIGH criterion should give you errors below 100m, which is not as good as GPS can be, but matches your needs.
You also need to monitor the status of your location provider, and switch to another provider if it gets unavailable or disabled by the user.
The passive provider may also be a good match for this kind of application: the idea is to use location updates whenever they are requested by another app and broadcast systemwide.
看起来我们在编写相同的应用程序;-)
这是我当前的实现。我的GPS上传应用程序仍处于beta测试阶段,因此可能会有许多可能的改进。但到目前为止,它似乎运行得很好。
/**
* try to get the 'best' location selected from all providers
*/
private Location getBestLocation() {
Location gpslocation = getLocationByProvider(LocationManager.GPS_PROVIDER);
Location networkLocation =
getLocationByProvider(LocationManager.NETWORK_PROVIDER);
// if we have only one location available, the choice is easy
if (gpslocation == null) {
Log.d(TAG, "No GPS Location available.");
return networkLocation;
}
if (networkLocation == null) {
Log.d(TAG, "No Network Location available");
return gpslocation;
}
// a locationupdate is considered 'old' if its older than the configured
// update interval. this means, we didn't get a
// update from this provider since the last check
long old = System.currentTimeMillis() - getGPSCheckMilliSecsFromPrefs();
boolean gpsIsOld = (gpslocation.getTime() < old);
boolean networkIsOld = (networkLocation.getTime() < old);
// gps is current and available, gps is better than network
if (!gpsIsOld) {
Log.d(TAG, "Returning current GPS Location");
return gpslocation;
}
// gps is old, we can't trust it. use network location
if (!networkIsOld) {
Log.d(TAG, "GPS is old, Network is current, returning network");
return networkLocation;
}
// both are old return the newer of those two
if (gpslocation.getTime() > networkLocation.getTime()) {
Log.d(TAG, "Both are old, returning gps(newer)");
return gpslocation;
} else {
Log.d(TAG, "Both are old, returning network(newer)");
return networkLocation;
}
}
/**
* get the last known location from a specific provider (network/gps)
*/
private Location getLocationByProvider(String provider) {
Location location = null;
if (!isProviderSupported(provider)) {
return null;
}
LocationManager locationManager = (LocationManager) getApplicationContext()
.getSystemService(Context.LOCATION_SERVICE);
try {
if (locationManager.isProviderEnabled(provider)) {
location = locationManager.getLastKnownLocation(provider);
}
} catch (IllegalArgumentException e) {
Log.d(TAG, "Cannot acces Provider " + provider);
}
return location;
}
编辑:这里是请求位置提供者定期更新的部分:
public void startRecording() {
gpsTimer.cancel();
gpsTimer = new Timer();
long checkInterval = getGPSCheckMilliSecsFromPrefs();
long minDistance = getMinDistanceFromPrefs();
// receive updates
LocationManager locationManager = (LocationManager) getApplicationContext()
.getSystemService(Context.LOCATION_SERVICE);
for (String s : locationManager.getAllProviders()) {
locationManager.requestLocationUpdates(s, checkInterval,
minDistance, new LocationListener() {
@Override
public void onStatusChanged(String provider,
int status, Bundle extras) {}
@Override
public void onProviderEnabled(String provider) {}
@Override
public void onProviderDisabled(String provider) {}
@Override
public void onLocationChanged(Location location) {
// if this is a gps location, we can use it
if (location.getProvider().equals(
LocationManager.GPS_PROVIDER)) {
doLocationUpdate(location, true);
}
}
});
// //Toast.makeText(this, "GPS Service STARTED",
// Toast.LENGTH_LONG).show();
gps_recorder_running = true;
}
// start the gps receiver thread
gpsTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Location location = getBestLocation();
doLocationUpdate(location, false);
}
}, 0, checkInterval);
}
public void doLocationUpdate(Location l, boolean force) {
long minDistance = getMinDistanceFromPrefs();
Log.d(TAG, "update received:" + l);
if (l == null) {
Log.d(TAG, "Empty location");
if (force)
Toast.makeText(this, "Current location not available",
Toast.LENGTH_SHORT).show();
return;
}
if (lastLocation != null) {
float distance = l.distanceTo(lastLocation);
Log.d(TAG, "Distance to last: " + distance);
if (l.distanceTo(lastLocation) < minDistance && !force) {
Log.d(TAG, "Position didn't change");
return;
}
if (l.getAccuracy() >= lastLocation.getAccuracy()
&& l.distanceTo(lastLocation) < l.getAccuracy() && !force) {
Log.d(TAG,
"Accuracy got worse and we are still "
+ "within the accuracy range.. Not updating");
return;
}
if (l.getTime() <= lastprovidertimestamp && !force) {
Log.d(TAG, "Timestamp not never than last");
return;
}
}
// upload/store your location here
}
需要考虑的事情:
不要太频繁地要求更新GPS,这会消耗电池电量。我目前
使用30分钟作为默认的应用程序。
增加“到最后已知位置的最小距离”检查。没有这个,你的分数
当GPS无法使用,位置被三角定位时,会“跳来跳去”吗
从发射塔传来的。或者您可以检查新位置是否在精度范围之外
最后一个已知位置的值。
这是我的解决方案,效果相当不错:
private Location bestLocation = null;
private Looper looper;
private boolean networkEnabled = false, gpsEnabled = false;
private synchronized void setLooper(Looper looper) {
this.looper = looper;
}
private synchronized void stopLooper() {
if (looper == null) return;
looper.quit();
}
@Override
protected void runTask() {
final LocationManager locationManager = (LocationManager) service
.getSystemService(Context.LOCATION_SERVICE);
final SharedPreferences prefs = getPreferences();
final int maxPollingTime = Integer.parseInt(prefs.getString(
POLLING_KEY, "0"));
final int desiredAccuracy = Integer.parseInt(prefs.getString(
DESIRED_KEY, "0"));
final int acceptedAccuracy = Integer.parseInt(prefs.getString(
ACCEPTED_KEY, "0"));
final int maxAge = Integer.parseInt(prefs.getString(AGE_KEY, "0"));
final String whichProvider = prefs.getString(PROVIDER_KEY, "any");
final boolean canUseGps = whichProvider.equals("gps")
|| whichProvider.equals("any");
final boolean canUseNetwork = whichProvider.equals("network")
|| whichProvider.equals("any");
if (canUseNetwork)
networkEnabled = locationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (canUseGps)
gpsEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
// If any provider is enabled now and we displayed a notification clear it.
if (gpsEnabled || networkEnabled) removeErrorNotification();
if (gpsEnabled)
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER));
if (networkEnabled)
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
if (desiredAccuracy == 0
|| getLocationQuality(desiredAccuracy, acceptedAccuracy,
maxAge, bestLocation) != LocationQuality.GOOD) {
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateBestLocation(location);
if (desiredAccuracy != 0
&& getLocationQuality(desiredAccuracy,
acceptedAccuracy, maxAge, bestLocation)
== LocationQuality.GOOD)
stopLooper();
}
public void onProviderEnabled(String provider) {
if (isSameProvider(provider,
LocationManager.NETWORK_PROVIDER))networkEnabled =true;
else if (isSameProvider(provider,
LocationManager.GPS_PROVIDER)) gpsEnabled = true;
// The user has enabled a location, remove any error
// notification
if (canUseGps && gpsEnabled || canUseNetwork
&& networkEnabled) removeErrorNotification();
}
public void onProviderDisabled(String provider) {
if (isSameProvider(provider,
LocationManager.NETWORK_PROVIDER))networkEnabled=false;
else if (isSameProvider(provider,
LocationManager.GPS_PROVIDER)) gpsEnabled = false;
if (!gpsEnabled && !networkEnabled) {
showErrorNotification();
stopLooper();
}
}
public void onStatusChanged(String provider, int status,
Bundle extras) {
Log.i(LOG_TAG, "Provider " + provider + " statusChanged");
if (isSameProvider(provider,
LocationManager.NETWORK_PROVIDER)) networkEnabled =
status == LocationProvider.AVAILABLE
|| status == LocationProvider.TEMPORARILY_UNAVAILABLE;
else if (isSameProvider(provider,
LocationManager.GPS_PROVIDER))
gpsEnabled = status == LocationProvider.AVAILABLE
|| status == LocationProvider.TEMPORARILY_UNAVAILABLE;
// None of them are available, stop listening
if (!networkEnabled && !gpsEnabled) {
showErrorNotification();
stopLooper();
}
// The user has enabled a location, remove any error
// notification
else if (canUseGps && gpsEnabled || canUseNetwork
&& networkEnabled) removeErrorNotification();
}
};
if (networkEnabled || gpsEnabled) {
Looper.prepare();
setLooper(Looper.myLooper());
// Register the listener with the Location Manager to receive
// location updates
if (canUseGps)
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 1000, 1,
locationListener, Looper.myLooper());
if (canUseNetwork)
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1000, 1,
locationListener, Looper.myLooper());
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
stopLooper();
}
}, maxPollingTime * 1000);
Looper.loop();
t.cancel();
setLooper(null);
locationManager.removeUpdates(locationListener);
} else // No provider is enabled, show a notification
showErrorNotification();
}
if (getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
bestLocation) != LocationQuality.BAD) {
sendUpdate(new Event(EVENT_TYPE, locationToString(desiredAccuracy,
acceptedAccuracy, maxAge, bestLocation)));
} else Log.w(LOG_TAG, "LocationCollector failed to get a location");
}
private synchronized void showErrorNotification() {
if (notifId != 0) return;
ServiceHandler handler = service.getHandler();
NotificationInfo ni = NotificationInfo.createSingleNotification(
R.string.locationcollector_notif_ticker,
R.string.locationcollector_notif_title,
R.string.locationcollector_notif_text,
android.R.drawable.stat_notify_error);
Intent intent = new Intent(
android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
ni.pendingIntent = PendingIntent.getActivity(service, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Message msg = handler.obtainMessage(ServiceHandler.SHOW_NOTIFICATION);
msg.obj = ni;
handler.sendMessage(msg);
notifId = ni.id;
}
private void removeErrorNotification() {
if (notifId == 0) return;
ServiceHandler handler = service.getHandler();
if (handler != null) {
Message msg = handler.obtainMessage(
ServiceHandler.CLEAR_NOTIFICATION, notifId, 0);
handler.sendMessage(msg);
notifId = 0;
}
}
@Override
public void interrupt() {
stopLooper();
super.interrupt();
}
private String locationToString(int desiredAccuracy, int acceptedAccuracy,
int maxAge, Location location) {
StringBuilder sb = new StringBuilder();
sb.append(String.format(
"qual=%s time=%d prov=%s acc=%.1f lat=%f long=%f",
getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
location), location.getTime() / 1000, // Millis to
// seconds
location.getProvider(), location.getAccuracy(), location
.getLatitude(), location.getLongitude()));
if (location.hasAltitude())
sb.append(String.format(" alt=%.1f", location.getAltitude()));
if (location.hasBearing())
sb.append(String.format(" bearing=%.2f", location.getBearing()));
return sb.toString();
}
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(int desiredAccuracy,
int acceptedAccuracy, int maxAge, Location location) {
if (location == null) return LocationQuality.BAD;
if (!location.hasAccuracy()) return LocationQuality.BAD;
long currentTime = System.currentTimeMillis();
if (currentTime - location.getTime() < maxAge * 1000
&& location.getAccuracy() <= desiredAccuracy)
return LocationQuality.GOOD;
if (acceptedAccuracy == -1
|| location.getAccuracy() <= acceptedAccuracy)
return LocationQuality.ACCEPTED;
return LocationQuality.BAD;
}
private synchronized void updateBestLocation(Location location) {
bestLocation = getBestLocation(location, bestLocation);
}
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);
}
我在互联网上搜索了一个更新的(去年)答案,使用谷歌建议的最新位置提取方法(使用FusedLocationProviderClient)。最后我想到了这个:
https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates
我创建了一个新项目,并复制了其中的大部分代码。繁荣。它的工作原理。我想没有任何不赞成的台词。
而且,据我所知,模拟器似乎没有GPS定位。它确实在日志中报告了这一点:“所有位置设置都满足要求。”
最后,如果你想知道(我做了),你不需要谷歌地图api密钥从谷歌开发控制台,如果你想要的只是GPS位置。
他们的教程也很有用。但我想要一个完整的一页教程/代码示例。他们的教程堆栈,但当你是新手时,你会感到困惑,因为你不知道你需要从前面的页面中获得什么。
https://developer.android.com/training/location/index.html
最后,记住这些事情:
我不仅要修改mainActivity.Java。我还必须修改Strings.xml, androidmanifest.xml和正确的build.gradle。还有activity_Main.xml(但这部分对我来说很简单)。
我需要添加像这样的依赖:实现'com.google.android.gms:play-services-location:11.8.0',并更新我的android studio SDK的设置,以包括谷歌播放服务。(文件设置外观系统设置android SDK SDK工具检查谷歌播放服务)。
更新:android模拟器似乎得到了一个位置和位置变化事件(当我改变sim设置中的值时)。但我最好的和第一个结果是在实际设备上。所以在实际设备上进行测试可能是最简单的。
最近重构得到的位置代码,学习了一些不错的思路,最后实现了一个比较完善的库和Demo。
@Gryphius的回答很好
//request all valid provider(network/gps)
private boolean requestAllProviderUpdates() {
checkRuntimeEnvironment();
checkPermission();
if (isRequesting) {
EasyLog.d("Request location update is busy");
return false;
}
long minTime = getCheckTimeInterval();
float minDistance = getCheckMinDistance();
if (mMapLocationListeners == null) {
mMapLocationListeners = new HashMap<>();
}
mValidProviders = getValidProviders();
if (mValidProviders == null || mValidProviders.isEmpty()) {
throw new IllegalArgumentException("Not available provider.");
}
for (String provider : mValidProviders) {
LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (location == null) {
EasyLog.e("LocationListener callback location is null.");
return;
}
printf(location);
mLastProviderTimestamp = location.getTime();
if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
finishResult(location);
} else {
doLocationResult(location);
}
removeProvider(location.getProvider());
if (isEmptyValidProviders()) {
requestTimeoutMsgInit();
removeUpdates();
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
getLocationManager().requestLocationUpdates(provider, minTime, minDistance, locationListener);
mMapLocationListeners.put(provider, locationListener);
EasyLog.d("Location request %s provider update.", provider);
}
isRequesting = true;
return true;
}
//remove request update
public void removeUpdates() {
checkRuntimeEnvironment();
LocationManager locationManager = getLocationManager();
if (mMapLocationListeners != null) {
Set<String> keys = mMapLocationListeners.keySet();
for (String key : keys) {
LocationListener locationListener = mMapLocationListeners.get(key);
if (locationListener != null) {
locationManager.removeUpdates(locationListener);
EasyLog.d("Remove location update, provider is " + key);
}
}
mMapLocationListeners.clear();
isRequesting = false;
}
}
//Compared with the last successful position, to determine whether you need to filter
private boolean isNeedFilter(Location location) {
checkLocation(location);
if (mLastLocation != null) {
float distance = location.distanceTo(mLastLocation);
if (distance < getCheckMinDistance()) {
return true;
}
if (location.getAccuracy() >= mLastLocation.getAccuracy()
&& distance < location.getAccuracy()) {
return true;
}
if (location.getTime() <= mLastProviderTimestamp) {
return true;
}
}
return false;
}
private void doLocationResult(Location location) {
checkLocation(location);
if (isNeedFilter(location)) {
EasyLog.d("location need to filtered out, timestamp is " + location.getTime());
finishResult(mLastLocation);
} else {
finishResult(location);
}
}
//Return to the finished position
private void finishResult(Location location) {
checkLocation(location);
double latitude = location.getLatitude();
double longitude = location.getLongitude();
float accuracy = location.getAccuracy();
long time = location.getTime();
String provider = location.getProvider();
if (mLocationResultListeners != null && !mLocationResultListeners.isEmpty()) {
String format = "Location result:<%f, %f> Accuracy:%f Time:%d Provider:%s";
EasyLog.i(String.format(format, latitude, longitude, accuracy, time, provider));
mLastLocation = location;
synchronized (this) {
Iterator<LocationResultListener> iterator = mLocationResultListeners.iterator();
while (iterator.hasNext()) {
LocationResultListener listener = iterator.next();
if (listener != null) {
listener.onResult(location);
}
iterator.remove();
}
}
}
}
完整的实现:
https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java
1.谢谢@Gryphius解决方案的想法,我也分享了完整的代码。
2.每次请求完成位置时,最好删除更新,否则手机状态栏会一直显示定位图标