(一)在使用位置服务之前,最好了解一下我们的对象,这样才能尽量少的犯错误。
问题:位置服务是什么?位置服务的有那些特性呢?
a.位置服务是什么?(百度百科)
对于位置定义有如下几种方法:
A)AOA(angle of arrival )指通过两个基站的交集来获取移动台(Mobile station)的位置;
B)TDOA(time difference of arrival)工作原来类似与GPS。通过一个移动台和多个基站交互的时间差来定位;
C)location signature位置标记。对每个位置区进行标识来获取位置;
D)卫星定位。
b.位置服务的有那些特性呢
A)定位方法是多样性的。
这就要求我们根据自己的情况去做选择,去做加减法,甚至要做些算法的处理。
B)位置信息的获取。
android位置服务仅是提供给我们绝对的位置,而如果需要地理信息的话就需要第三方平台的支持(高德,百度地图等)
C)定位精度与定位时间的矛盾关系。
时间与质量永远是矛盾的,想要获取高精度的定位信息那就必然需要足够的时间作保证。
(二)使用位置服务
a.获取位置服务
LocationManager mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
另外应用在启动之初就已经拿到了system_service中的位置服务。
frameworks/base/core/java/android/app/SystemServiceRegistry.java
registerService(Context.LOCATION_SERVICE, LocationManager.class,
new CachedServiceFetcher<LocationManager>() {
@Override
public LocationManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE);
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}});
b.注册位置服务
注册位置服务,开始获取位置信息。
A)需要申请权限:
<!-- 用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 用于访问GPS定位 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 用于获取其他定位 -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
B)位置信息权限检测,位置服务功能使能检查,开始申请位置信息。
public void requestLocation() {
if (ActivityCompat.checkSelfPermission(MyApp.mAppContext, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if (!mLocationManager.isProviderEnabled(LOCATION_PROVIDER)) {
Toast.makeText(MyApp.mAppContext, "无法使用" + LOCATION_PROVIDER + "进行定位", Toast.LENGTH_LONG).show();
}else{
mLocationManager.requestLocationUpdates(
LOCATION_PROVIDER,
GPS_LOCATION_UPDATE_TIME, 0, mLocationListener);
}
} else {
Toast.makeText(MyApp.mAppContext, "需要位置权限", Toast.LENGTH_LONG).show();
}
}
private LocationListener mLocationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (null == location) {
return;
}
if (location != null) {
StringBuffer sb = new StringBuffer();
sb.append("onLocationChanged :" + "\n");
sb.append("经 度 : " + location.getLongitude() + "\n");
sb.append("纬 度 : " + location.getLatitude() + "\n");
sb.append("提供者 : " + location.getProvider() + "\n");
sb.append("精 度 : " + location.getAccuracy() + "米" + "\n");
sb.append("速 度 : " + location.getSpeed() + "米/秒" + "\n");
Log.d("lishuo", "onLocationChanged " + sb.toString());
} else {
Log.d("lishuo", "onLocationChanged : null !");
}
if (location.getLatitude() == 0.0
&& location.getLongitude() == 0.0) {
// Hack to filter out 0.0,0.0 locations
return;
}
lastLocation = new Location(location);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
};
requestLocationUpdates的定义与注解
frameworks/base/location/java/android/location/LocationManager.java
/**
* Register for location updates using the named provider, and a
* pending intent.
*
* <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
* for more detail on how to use this method.
*
* @param provider the name of the provider with which to register
* @param minTime minimum time interval between location updates, in milliseconds
* @param minDistance minimum distance between location updates, in meters
* @param listener a {@link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* on this device
* @throws IllegalArgumentException if listener is null
* @throws RuntimeException if the calling thread has no Looper
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(String provider, long minTime, float minDistance,
LocationListener listener) {
checkProvider(provider);
checkListener(listener);
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, minTime, minDistance, false);
requestLocationUpdates(request, listener, null, null);
}
到此为止整个获取GPS位置信息的过程也就结束了,逻辑大部分已经在LocationManagerService中做了整合实现,应用层的使用方法很简单。但是以上获取来的数据对于大部分应用对位置信息的数据精度要求一般是不能满足要求的,这个时候就要去获取更高精度的数据了。
(二)位置数据精度处理
目前手机端使用到的定位方法为lbs基站定位以及GPS定位的方法,以下是两种定位方式的描述:
a. 基站定位是通过得到不同基站下行导频的TOA(Time of Arrival,到达时刻),根据该测量结果并结合基站的坐标,用来计算出移动端的位置。这种方法受附件基站数,建筑物影响较大,一般有100~200米的误差,定位响应时间为3~6s之间。
b. GPS定位是移动端通过接受卫星不断发射的星历参数和时间信息,然后根据三角公式计算就可以得到移动端的位置,收到三颗卫星的数据可进行2D定位(经度、纬度),当收到四颗卫星的数据时则可进行3D定位(经度、纬度及高度)。GPS定位数据受卫星信号,卫星数量的影响,当卫星信号较好时,其精度能保证在几米至几十米,响应时间大约为3s.
针对以上的两种定位方式,需要根据不同的环境,去做选择,当然每种定位方法也有其独特性,获取数据的精度也有不同的处理。
A)定位方式的选择。
定位方式的选择时,一般是选择GPS优先,LBS次之。在GPS定位开始时需要设置一个超时检测,当GPS定位超时后,使用LBS去定位。(此处只使用了超时判断,在获取GPS定位数据时也可以通过GPS的精度,卫星强度取切换定位方式)。
private void timeOut() {
timerOut = new Timer();
timerOut.schedule(new TimerTask() {
public void run() {
midTime--;
Log.d("GPSLocation", "TimerTask :" + midTime);
if (midTime <= 0) {
midTime = TIMEOUT_TIME;
uploadLocation();//GPS定位超时后去选择LBS定位
}
}
}, 0, 1000);
}
B)针对GPS定位时间的优化。
当上层应用软件开始请求GPS定位后,在2~3秒之后Gps会反馈第一个数据,之后若GPS定位没有停止,那么大约会每秒吐出1个数据(此为卫星信号很好的情况下)。如下图所示的一些数据,发现前面几个点偏移较大,后面的点越来越收敛,越来越接近实际距离(F点位第一次定位的点,C点位最后一个点)。
普遍的GPS第一个数据在精度上是很差的,对于高精度的定位是没有办法满足要求的,而且GPS从热启动到连续的吐出数据时,随着时间的累积GPS数据的精度会越来越高,定位的效果也会越来越好。所有定位时可以由获取一个数据改为获取多次数据,取最后的数据。
C)针对数据漂移的优化
通过上图的GPS测试也可发现:当GPS稳定的时候,其定位坐标(经纬度)也经常在变,偶尔变化还比较大,业内人士把这种现象称为“漂移”。
数据漂移在Gps定位的影响中是无法完全移除的,但是其在静止定位过程中影响也较大,目前应用层做到的只是尽量减少漂移的数据对整体数据的影响,通过获取较多的数据,根据数据量,数据精度,剔除可能存在的飘逸数据,以降低“漂移”对应用层获取的数据的影响。同时也可以通过相应的算法处理数据,以获取较精确的数据。
android P中系统会默认限制第三方应用在后台状态下获取数据,后台获取数据时只会突出一个数据,要想后台获取正常数据,需要移除第三方应用位置服务后台限流功能(添加限流白名单)。
diff --git a/base/services/core/java/com/android/server/LocationManagerService.java b/base/services/core/java/com/android/server/LocationManagerService.java
@Override
@@ -1870,6 +1919,7 @@ public class LocationManagerService extends ILocationManager.Stub {
SystemConfig.getInstance().getAllowUnthrottledLocation());
mBackgroundThrottlePackageWhitelist.addAll(
Arrays.asList(setting.split(",")));
+ mBackgroundThrottlePackageWhitelist.add("com.location.android.poc");
}
默认系统UID、白名单、系统应用AndroidManifest.xml文件字段也有同样效果:
private boolean isThrottlingExemptLocked(Identity identity) {
if (identity.mUid == Process.SYSTEM_UID) {
return true;
}
if (mBackgroundThrottlePackageWhitelist.contains(identity.mPackageName)) {
return true;
}
for (LocationProviderProxy provider : mProxyProviders) {
if (identity.mPackageName.equals(provider.getConnectedPackageName())) {
return true;
}
}
return false;
}