注意:本指南中描述的策略适用于android.location中的平台位置API。 Google定位服务API是Google Play服务的一部分,它提供了一个更强大的高级框架,可自动处理位置提供商,用户移动和位置准确性。 它还可以根据您提供的功耗参数来处理位置更新调度。 在大多数情况下,通过使用Location Services API,您将获得更好的电池性能以及更准确的准确性。
要了解有关位置服务API的更多信息,请参阅Android的Google定位服务。
知道用户在哪里允许您的应用程序更智能,并向用户提供更好的信息。 在为Android开发位置感知应用程序时,您可以使用GPS和Android的网络位置提供程序来获取用户位置。 虽然GPS是最准确的,但它只能在户外使用,它很快消耗电池电量,并且不会像用户想要的那样快速返回位置。 Android的网络位置提供商使用单元塔和Wi-Fi信号确定用户位置,以室内和室外的方式提供位置信息,响应更快,并且使用更少的电池电量。 要获取应用程序中的用户位置,您可以同时使用GPS和网络位置提供程序,也可以同时使用一个。
一、确定用户位置的挑战
从移动设备获取用户位置可能很复杂。 位置读取(不管来源)可能包含错误并且不准确的原因有几个。 用户位置的一些错误来源包括:
1、多个位置来源
GPS,Cell-ID和Wi-Fi都可以为用户提供线索。 确定哪些使用和信任是精确度,速度和电池效率的权衡问题。
2、用户移动
由于用户位置发生变化,您必须经常重新估算用户位置来解释移动问题。
3、不一样的准确性
来自每个位置来源的位置估算值的准确性不一致。 从一个来源获得10秒前的位置可能比来自另一个或相同来源的最新位置更准确。
这些问题可能难以获得可靠的用户位置阅读。 本文档提供信息,以帮助您应对这些挑战,以获得可靠的位置阅读。 它还提供您可以在应用程序中使用的想法,为用户提供准确和响应的地理位置体验。
二、请求位置更新
在解决上述某些位置错误之前,以下介绍如何在Android上获取用户位置。
通过回调,在Android中获取用户位置。 您表示您希望通过调用requestLocationUpdates()从LocationManager(“位置管理器”)接收位置更新,并传递一个LocationListener。 您的LocationListener必须实现位置管理器在用户位置更改时或服务状态更改时调用的多个回调方法。
例如,以下代码显示了如何定义LocationListener和请求位置更新:
// Acquire a reference to the system Location Manager
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location provider.
makeUseOfNewLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
requestLocationUpdates()中的第一个参数是要使用的位置提供程序的类型(在这种情况下,是基于单元塔和基于Wi-Fi的位置的网络位置提供程序)。 您可以使用第二个和第三个参数来控制收听者接收更新的频率 - 第二个是通知之间的最短时间间隔,第三个是通知之间的距离的最小变化 - 尽可能频繁地将位置通知设置为零。 最后一个参数是您的LocationListener,它接收位置更新的回调。
要从GPS提供商请求位置更新,请使用GPS_PROVIDER而不是NETWORK_PROVIDER。 您还可以通过对NETWORK_PROVIDER发出两次呼叫requestLocationUpdates()和一次针对GPS_PROVIDER来请求GPS和网络位置提供商的位置更新。
一)、请求位置权限
为了从NETWORK_PROVIDER或GPS_PROVIDER接收位置更新,您必须通过在Android清单文件中分别声明ACCESS_COARSE_LOCATION或ACCESS_FINE_LOCATION权限来请求用户的权限。 没有这些权限,您的应用程序将在运行时请求位置更新时失败。
如果您使用的是NETWORK_PROVIDER和GPS_PROVIDER,那么您只需要请求ACCESS_FINE_LOCATION权限,因为它包含两个提供者的权限。 ACCESS_COARSE_LOCATION的权限仅允许访问NETWORK_PROVIDER。
注意:如果您的应用程序针对Android 5.0(API级别21)或更高版本,则必须声明您的应用程序在清单文件中使用android.hardware.location.network或android.hardware.location.gps硬件功能,具体取决于您的 应用程序从NETWORK_PROVIDER或GPS_PROVIDER接收位置更新。 如果您的应用程序从这些位置提供商来源收到位置信息,则需要声明该应用程序在应用程序清单中使用这些硬件功能。 在Android 5.0(API 21)之前运行验证的设备上,请求ACCESS_FINE_LOCATION或ACCESS_COARSE_LOCATION权限包含对位置硬件功能的隐含请求。 但是,请求这些权限不会自动请求Android 5.0(API级别21)及更高版本的位置硬件功能。
以下代码示例演示如何在从设备的GPS读取数据的应用程序的清单文件中声明权限和硬件功能:
<manifest ... >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
...
</manifest>
三、定义最佳性能模型
基于位置的应用程序现在是常见的,但是由于不太优化的准确性,用户移动,获取位置的多种方法以及节省电池,获取用户位置的愿望是复杂的。 为了克服在保持电池电量的同时获得良好的用户位置的障碍,您必须定义一个一致的模型,指定应用程序如何获取用户位置。 此模式包括何时启动和停止监听更新以及什么时候使用缓存的位置数据。
一)、用于获取用户位置的流程
以下是获取用户位置的典型流程:
1、启动app。
2、稍后,开始监听所需位置提供商的更新。
3、通过过滤出新的但不太准确的修复来保持位置的“当前最佳估计”。
4、停止监听位置更新。
5、利用最后的最佳位置估算。
图1在时间轴上演示了该模型,可视化应用程序侦听位置更新的时间段以及在此期间发生的事件。
图1.表示应用程序侦听位置更新的窗口的时间轴。
窗口的这种模式 - 在哪些位置更新被接收到帧时,您在应用程序中添加基于位置的服务时需要做出的许多决定。
二)、决定何时开始监听更新
一旦应用程序启动,或者只有在用户激活某个功能之后,您可能需要开始监听位置更新。 请注意,监听位置修复的长时间窗口可能会消耗大量电池电量,但短时间可能无法提供足够的准确性。
如上所述,您可以通过调用requestLocationUpdates()开始监听更新:
String locationProvider = LocationManager.NETWORK_PROVIDER;
// Or, use GPS location data:
// String locationProvider = LocationManager.GPS_PROVIDER;
locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
三)、使用最后一个已知位置快速修复
您的位置侦听器接收第一个位置修复所需的时间通常太长,以至于用户等待。 在您向位置监听器提供更准确的位置之前,您应该通过调用getLastKnownLocation(String)来使用缓存的位置:
String locationProvider = LocationManager.NETWORK_PROVIDER;
// Or use LocationManager.GPS_PROVIDER
Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
四)、决定何时停止监听更新
根据您的应用,决定何时不再需要新修复的逻辑可能从非常简单到非常复杂。 获取位置和使用位置之间的短暂差距提高了估计的准确性。 始终注意长时间的聆听会消耗大量电池电量,因此,只要拥有所需的信息,您应该通过调用removeUpdates(PendingIntent)来停止监听更新:
// Remove the listener you previously added
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);
}
六)、调整模型以节省电池和数据交换
当您测试您的应用程序时,您可能会发现您的模型提供良好的位置和良好的性能需要一些调整。 这里有一些你可能会改变,以找到两者之间的良好平衡。
1、减小窗口的大小
您收听位置更新的较小窗口意味着与GPS和网络位置服务的交互更少,从而保持电池寿命。 但是它也允许更少的选择最佳估计的位置。
2、将位置提供程序设置为不频繁地返回更新
降低窗口期间出现新更新的速率也可以提高电池效率,但是要以准确性为代价。 权衡的价值取决于您的应用程序的使用方式。 您可以通过增加requestLocationUpdates()中指定间隔时间和最小距离更改的参数来降低更新速率。
3、限制一组提供者
根据您的应用程序使用的环境或所需的准确度,您可以选择仅使用网络位置提供程序或仅使用GPS,而不是两者。 与其中一个服务相互作用可以以潜在的准确性降低电池使用。
四、常见应用案例
您可能需要在应用程序中获取用户位置的原因很多。 以下是您可以使用用户位置丰富您的应用程序的几种情况。 每个场景还描述了当您开始和停止聆听位置时的良好做法,以便获得良好的阅读并有助于延长电池寿命。
一)、使用位置标记用户创建的内容
您可能正在创建一个用户创建的内容被标记为某个位置的应用程序。 想想用户分享他们的当地经验,发布餐厅的评论,或录制一些可以增加他们当前位置的内容。 在图2中可以看出关于定位服务的这种互动可能如何发生的模型。
图2.当用户消耗当前位置时,表示获取用户位置的窗口的时间线停止。
这与以前的代码中获取用户位置的模型相一致(图1)。 为了获得最佳的位置精确度,您可以选择在用户开始创建内容时甚至应用程序启动时开始监听位置更新,然后在内容准备好发布或记录时停止监听更新。 您可能需要考虑创建内容的典型任务需要多长时间,并判断此持续时间是否可以有效地收集位置估计。
二)、帮助用户决定去哪里
您可能正在创建一个应用程序,尝试为用户提供一组关于去哪里的选项。 例如,您希望提供附近餐馆,商店和娱乐的列表,并根据用户位置更改建议顺序。
为了适应这样的流程,您可以选择:
1、当获得新的最佳估计值时重新排列建议。
2、如果建议顺序稳定,请停止收听更新
这种模型在图3中可视化。
图3.表示每次用户位置更新时更新动态数据集的窗口的时间轴。
五、提供模拟位置数据
当您开发应用程序时,您一定需要测试您的模型获得用户位置的效果。 这是使用真正的Android设备最容易完成的。 但是,如果您没有设备,您仍然可以通过在Android模拟器中模拟位置数据来测试基于位置的功能。 发送应用程序模拟位置数据有三种不同的方式:在模拟器控制台中使用Android Studio,DDMS或“geo”命令。
注意:提供模拟位置数据作为GPS位置数据注入,因此您必须从GPS_PROVIDER请求位置更新,以便模拟位置数据工作。
一)、使用Android Studio
选择工具> Android> AVD管理器。 在“Android虚拟设备管理器”窗口中,选择您的AVD,然后在“操作”列中选择绿色播放箭头,在模拟器中启动它。
然后,选择工具> Android> Android设备监视器。 在“Android设备监视器”窗口中选择“模拟器控制”选项卡,并在“位置控制”下输入GPS坐标作为单独的拉/长坐标,具有路线播放的GPX文件或多个地方标记的KML文件。
二)、使用DDMS
使用DDMS工具,您可以通过几种不同的方式模拟位置数据:
1、手动向设备发送单独的经度/纬度坐标。
2、使用描述用于播放设备的路由的GPX文件。
3、使用描述各个地方标记的KML文件,以便顺序播放设备。
有关使用DDMS欺骗位置数据的更多信息,请参阅使用DDMS。
三)、在仿真器控制台中使用“geo”命令
从命令行发送模拟位置数据:
1、在Android模拟器中启动应用程序,并在SDK的/ tools目录中打开终端/控制台。
2、连接到模拟器控制台:
telnet localhost <console-port>
3、发送位置数据:
1)、geo修复发送固定的地理位置。
该命令接受十进制度的经度和纬度,并以米为单位选择高度。 例如:
geo fix -121.45356 46.51119 4392
2)、geo nmea发送NMEA 0183句子。
该命令接受单个NMEA类型'$ GPGGA'(修复数据)或'$ GPRMC'(传输数据)。 例如:
geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
有关如何连接到仿真器控制台的信息,请参阅使用 使用仿真控制台。