翻译:Lauhoman http://blog.csdn.net/sinat_26599509/
官方API英文原文:https://developer.android.com/guide/topics/location/strategies.html
注意:本指南中描述的策略仅仅适用于android.location中的定位API。 Google位置服务API是Google Play服务的一部分,提供了更强大的高级框架,可自动处理位置提供商、用户移动和位置精确度。 它还根据您提供的功耗参数处理位置更新计划。 在大多数情况下,您可以通过使用位置服务API获得更好的电池性能以及更合适的精度。要详细了解位置服务API,请参阅Android版Google定位服务。
从移动设备获得用户位置是极其复杂的,以下几个原因会导致位置读取的错误或不准确:
多种位置来源:GPS、小区ID和Wi-Fi可以各自提供用户位置的线索,而确定使用哪个服务取决于精度、速度和电池效率的权衡。
用户移动:因为用户位置有可能改变,所以必须经常重新确定用户位置来检测移动。
变化精度:来自每个定位方式的位置估计值的准确性并不一致,从一个来源获得的10秒前的位置可能反而比来自另一个或相同来源的最新位置更精确。
这些挑战都导致了定位服务的困难,本文档提供的信息可帮助您应对这些挑战,以获得可靠的位置读数。它还提供如何在您自己的应用中为用户提供准确、可靠地理位置的经验。
在解决上述定位误差之前,我们先来介绍一下如何在安卓设备中获得用户的位置信息。
安卓中位置信息的获取依赖于回调策略,当你需要获得位置信息的时候,你需要通过requestLocationUpdates()
函数向LocationManager(即”Location Manager位置管家”)发出请求,告知它你需要位置信息的更新,并传递一个监听器LocationListener给它。你的LocationListener
必须要完成对一系列回调函数的实现,当位置改变或者定位服务的状态改变,这些函数将会被LocationManager
调用。
举个例子来说,下面的代码展示了如何定义一个LocationListener并请求定位更新:
// 获取对系统位置管理器的引用
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// 定义一个回应位置更新的监听器
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// 当一个新的位置由网络位置提供时调用
makeUseOfNewLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
// 把这个监听器注册到Location Manager上来接受位置更新
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
其中requestLocationUpdates()
的第一个参数是位置提供来源的类型(在这个例子中定位来源是网络定位—来自于信号塔和WiFi)。你可以通过第二个和第三个参数自定义你的监听器的位置更新接收频率,其中第二个参数是两次定位最短的时间间隔、第三个参数是两次定位最小位置变化,这个例子中设置为0即尽可能的频繁更新位置。而最后一个参数则是你想要注册到LocationManager
上的LocationListener
。
如果想要通过GPS传感器来获取位置,使用GPS_PROVIDER
来代替NETWORK_PROVIDER
。你也可以通过调用requestLocationUpdates()
函数两次来同时使用GPS和网络请求位置更新—一个使用GPS_PROVIDER
,另一个使用NETWORK_PROVIDER
。
为了从GPS_PROVIDER
和NETWORK_PROVIDER
来定位,你必须在安卓应用manifest文件中分别声明ACCESS_COARSE_LOCATION
、ACCESS_FINE_LOCATION
这两个向用户请求定位的权限。如果没有这些权限,你的应用将会在位置请求上失败。
如果你正同时使用GPS_PROVIDER
和NETWORK_PROVIDER
,那么你只需要请求一次ACCESS_FINE_LOCATION
权限即可,因为这个权限的范围包括了以上两种来源。而ACCESS_COARSE_LOCATION
权限则只允许使用NETWORK_PROVIDER
。
警告:如果您的应用是针对ndroid 5.0(API级别21)或更高版本,则必须在应用的manifest文件中声明使用android.hardware.location.network或android.hardware.location.gps硬件功能,具体取决于您的应用是否接收来自NETWORK_PROVIDER或GPS_PROVIDER的位置更新。 如果您的应用从这些位置来源中获取位置信息,则需要在您的manifest文件中声明您的应用使用了这些硬件功能。 对于运行Android 5.0(API 21)之前版本的设备,请求ACCESS_FINE_LOCATION或ACCESS_COARSE_LOCATION权限包含了位置硬件功能的隐含请求。 但是,请求这些权限不会自动请求Android 5.0(API级别21)及更高版本上的位置硬件功能。
下面的代码样例演示了一个从GPS传感器获取数据的应用如何在manifest文件中声明权限和硬件信息:
...
...
基于定位服务的应用现在遍地都是了,但是由于总是低于理想的精度、用户的移动、获取位置方式的多样性和节电需求,获取用户位置是极为复杂的。为了跨越节电的情况下提供较准确的定位这个挑战,你必须选择一系列合适的模型来决定你的应用怎么获取位置。这个决策模型包括了你什么时候开始、停止监听位置更新和什么时候使用缓存的位置数据。
下面是一个典型的获取用户位置程序的流程图:
这样的窗口模型,需要你在构建需要位置服务的应用程序做出许多重要决定。
你一定想要让监听器从你的应用启动开始或者仅仅在用户打开某项功能时就开始监听,但注意较长的定位监听窗口会消耗大量的电量,而短周期的定位服务可能没有那么精确。
正如上述,你可以通过调用requestLocationUpdates()
函数来开始监听位置更新:
String locationProvider = LocationManager.NETWORK_PROVIDER;
// 或者使用GPS传感器提供的位置信息:
// String locationProvider = LocationManager.GPS_PROVIDER;
locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
位置监听器接收到第一个准确定位往往需要的时间对于使用者来说太长,所以在得到一个很准确的位置之前最好先通过getLastKnownLocation(String)
提供给用户一个缓存好的位置信息:
String locationProvider = LocationManager.NETWORK_PROVIDER;
// 或者使用LocationManager.GPS_PROVIDER
Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
决定什么时候不再需要新的定位的逻辑依赖于你的应用,这可能相当简单也可能十分复杂。在获取位置和使用位置之间的短暂空隙能够提高定位的准确性,并且一定要时刻记住长时间的监听位置更新必然会消耗大量电量,所以一旦你获得了你需要的位置信息,你就应该通过removeUpdates(PendingIntent)
来停止监听位置更新:
// 注销掉注册在LocationManager上的LocationListener
locationManager.removeUpdates(locationListener);
你一定希望最近一次的定位时最准确的,但是由于定位的多样性,大多数最近的定位都不是最佳的。你应该制定一些标准来选择定位数据,这种标准往往也会由于应用和测试的实际情况而呈现出多样性。
下面是一些用来确定定位精度的步骤:
1. 检查新得到的位置是否完全比原来的位置数据更新;
2. 检查新得到的位置在精度上是比原来的位置数据更准确还是更差;
3. 检查新的位置信息来源于何种位置来源,决定你更愿意相信哪个。
一个精心准备的例子如下:
private static final int TWO_MINUTES = 1000 * 60 * 2;
/** Determines whether one Location reading is better than the current Location fix
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// 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 true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// 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 true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
当你测试你的应用时,你会发现为了提供较好的位置和较好的性能表现需要经过一系列的调整,下面是一些改变两者之间的平衡的东西:
一个较小的监听窗口意味着较少与网络或GPS服务的交互,同时,能节省电量。但是它必须保证仍然允许从较少的定位信息中获得最佳的数据。
减少新位置更新的出现频率同样能改善电池的表现,但是代价是准确度。更新频率取决于你的应用程序是怎样使用定位信息的。你可以通过更改requestLocationUpdates()
中的参数来减少更新率—即增大间隔时间或者间隔距离。
根据你的应用的使用环境,你要选择仅仅使用网络定位或者仅仅使用GPS定位来取代两者都用。只使用一种服务能有效的在较为潜在的精准度风险中减少电池消耗。
有很多原因可能需要在应用程序中获取用户位置。 以下是一些情况,您可以使用用户位置丰富您的应用程序。 每个场景还描述了当您应该开始和停止监听位置时的良好做法,以便获得良好的定位数据并帮助保持电池寿命。
您可能正在创建一个应用程序,其中用户创建的内容标记有位置信息。 用户能分享他们的本地体验、发布对餐厅的评论或记录一些可以用他们当前位置增强的内容。 关于位置服务的这种交互应该如何处理的模型可以参考下图:
这与之前在代码中如何获得用户位置的模型相呼应。 为了获得最佳的位置精确度,您可以选择在用户开始创建内容时或甚至在应用程序启动时开始监听位置更新,然后在内容准备发布或记录时停止监听更新。 您可能需要考虑创建内容的典型任务需要花费多长时间,并判断此持续时间是否允许收集新的位置数据。
您可能正在创建一个应用程序,试图向用户提供一组有关去哪里的选项。 例如,您希望提供附近的餐馆,商店和娱乐的列表,并且根据用户位置更改建议顺序。
为了达成如下的流程,你可能要选择:
这样的模型在下图中可以体现: