问题:
尽快将用户的当前位置置于阈值内,同时节省电池电量 .
为什么问题出在问题上:
首先,android有两个提供商;网络和GPS . 有时网络更好,有时GPS更好 .
"better"我的意思是速度与准确度之比 .
我愿意牺牲几米精度,如果我能在几乎立即获得位置并且不打开GPS .
其次,如果您要求更新位置更改,则在当前位置稳定时不会发送任何内容 .
谷歌有一个确定"best"位置的例子:http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
但我认为它不应该接近它应该/可能的好 .
我有点困惑为什么谷歌没有标准化的位置API,开发者不应该关心位置来自哪里,你应该只指定你想要的东西,手机应该为你选择 .
我需要帮助的是:
我需要找到一个很好的方法来确定“最佳”位置,也许是一些启发式或者可能通过某些第三方库 .
This does not mean determine the best provider!
我可能会使用所有提供商并选择其中最好的 .
应用程序的背景:
该应用程序将以固定间隔收集用户的位置(假设每10分钟左右)并将其发送到服务器 .
该应用程序应尽可能节省电池,并且位置应具有X(50-100?)米的精度 .
目标是以后能够在 Map 上绘制白天用户的路径,因此我需要足够的准确性 .
其他:
您认为对于期望和接受的准确度的合理 Value 是什么?
我一直在使用100米接受,并且根据需要使用30米,这要问多少?
我以后在 Map 上的路径是_876669 .
期望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);
}
12 回答
目前我正在使用,因为这对我的应用程序获取位置和计算距离是可靠的......我正在使用这个用于我的出租车应用程序 .
使用谷歌开发人员开发的融合API,GPS传感器,磁力计,加速度计的融合也使用Wifi或小区位置来计算或估计位置 . 它还能够准确地在建筑物内部提供位置更新 . 详细信息请链接https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi
嗨,这是一个链接,它将能够提供整个源代码以整合gps位置的链接,该位置将能够通过gps跟踪任何人,并且ti将通知:
喜欢:http://code.google.com/p/mytracks/
看起来我们正在编写相同的应用程序;-)
这是我目前的实施 . 我仍处于GPS上传应用程序的beta测试阶段,因此可能会有许多改进 . 但到目前为止似乎工作得很好 .
Edit: 这是请求来自位置提供者的定期更新的部分:
Things to consider:
不要经常请求GPS更新,它会耗尽电池电量 . 我目前使用30分钟默认为我的应用程序 .
添加“到最后已知位置的最小距离”检查 . 如果没有这个,当GPS不可用时,你的点将“跳转”,并且位置正从手机信号塔进行三角测量 . 或者您可以检查新位置是否超出上一个已知位置的准确度值 .
要为您的应用选择正确的位置提供商,您可以使用Criteria对象:
阅读requestLocationUpdates的文档,了解有关如何考虑参数的更多详细信息:
更多想法
您可以使用Location.getAccuracy()监视位置对象的精度,它以米为单位返回位置的估计精度 .
Criteria.ACCURACY_HIGH
标准应该给你100米以下的误差,这不如GPS可以,但符合你的需要 .您还需要监控位置提供程序的状态,并在用户不可用或禁用时切换到其他提供程序 .
passive provider也可能是这种应用程序的一个很好的匹配:想法是在其他应用程序请求并在系统范围内广播时使用位置更新 .
Answering the first two points :
GPS将 always 为您提供更精确的位置,如果它已启用且周围没有厚墙 .
如果位置没有改变,那么你可以调用getLastKnownLocation(String)并立即检索位置 .
Using an alternative approach :
您可以尝试使用 cell id 或所有相邻单元格
您可以通过几个开放数据库(例如,http://www.location-api.com/或http://opencellid.org/)引用单元格位置
策略是在读取位置时读取塔ID列表 . 然后,在下一个查询中(在您的应用中10分钟),再次阅读它们 . 如果至少有一些塔是相同的,那么使用
getLastKnownLocation(String)
是安全的 . 如果他们不是,那么等待onLocationChanged()
. 这避免了对位置的第三方数据库的需要 . 你也可以试试this approach .这个是我的解决方案,运作得相当好:
位置准确性主要取决于所使用的位置提供商:
GPS - 将为您提供几米精度(假设您有GPS接收)
Wifi - 精确到几百米
Cell Network - 会给你带来非常不准确的结果(我已经看到了4公里偏差......)
如果您正在寻找它的准确性,那么GPS是您唯一的选择 .
我已经阅读了一篇非常有益的文章here .
至于GPS超时 - 60秒应该足够了,在大多数情况下甚至太多了 . 我认为30秒是可以的,有时甚至不到5秒......
如果您只需要一个位置,我建议您在
onLocationChanged
方法中,一旦收到更新,您将取消注册听众并避免不必要地使用GPS .Android-ReactiveLocation库是另一个处理Android位置的好库 .
https://github.com/mcharmas/Android-ReactiveLocation
我使用谷歌建议的最新位置拉动方法(使用FusedLocationProviderClient)在互联网上搜索更新(过去一年)的答案 . 我终于登陆了这个:
https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates
我创建了一个新项目并复制了大部分代码 . 繁荣 . 有用 . 我认为没有任何弃用的行 .
此外,我知道,模拟器似乎没有获得GPS位置 . 它确实在日志中报告了这一点:“满足所有位置设置 . ”
最后,如果你想知道(我做过),你不需要谷歌开发者控制台的谷歌 Map api密钥,如果你想要的只是GPS位置 .
他们的教程也很有用 . 但是我想要一个完整的单页教程/代码示例 . 他们的教程堆栈,但是当你刚接触到它时会感到困惑,因为你不知道从早期页面需要哪些部分 .
https://developer.android.com/training/location/index.html
最后,记住这样的事情:
我不仅要修改mainActivity.Java . 我还必须修改Strings.xml,androidmanifest.xml和正确的build.gradle . 还有你的activity_Main.xml(但那部分对我来说很容易) .
我需要添加像这样的依赖项:implementation'com.google.android.gms:play-services-location:11.8.0',并更新我的android studio SDK的设置以包含google play服务 . (文件设置外观系统设置android SDK SDK工具检查google play服务) .
更新:Android模拟器似乎确实获得了位置和位置更改事件(当我更改了sim的设置中的值) . 但我最好的和最初的结果是在一个实际的设备上 . 因此,在实际设备上进行测试可能最容易 .
根据我的经验,我发现最好使用GPS定位,除非它不可用 . 我不太了解其他位置提供商,但我知道对于GPS来说,有一些技巧可以用来给出一些贫民窟精确度量 . 海拔通常是一个标志,所以你可以检查荒谬的 Value 观 . Android位置修复程序有准确度衡量标准 . 此外,如果您可以看到使用的卫星数量,这也可以表明精度 .
一个有趣的方法来更好地了解准确性可能是非常迅速地要求一组修复,例如~1 /秒持续10秒然后再睡一两分钟 . 我去过的一次谈话导致相信一些Android设备无论如何都会这样做 . 然后你会淘汰异常值(我听过这里提到的卡尔曼滤波器)并使用某种中心策略来获得单一修复 .
显然,你到达这里的深度取决于你的要求有多难 . 如果您对获得最佳位置有特别严格的要求,我想您会发现GPS和网络位置与苹果和橙子相似 . GPS也可能因设备而异 .
最近重构了获取代码的位置,学习了一些好的想法,最后实现了一个比较完善的库和Demo .
@Gryphius的回答很好
完成实施:https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java
1.感谢@Gryphius解决方案的想法,我也分享了完整的代码 .
2.每次请求完成位置,最好删除更新,否则手机状态栏将始终显示定位图标
Skyhook(http://www.skyhookwireless.com/)的位置提供商比Google提供的标准提供商快得多 . 这可能就是你要找的东西 . 我不隶属于他们 .