(一)在使用位置服務之前,最好了解一下我們的對象,這樣才能儘量少的犯錯誤。
問題:位置服務是什麼?位置服務的有那些特性呢?
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;
}