Location Strategies
定位的實現
在Android系統上實現定位主要是通過GPS或者是基於網絡的定位方法。
GPS是精度最高的,但是它只在戶外有用,並且很耗電,並且首次定位花費時間較長。
基於網絡的定位利用通信網絡蜂窩基站和Wi-Fi信號,這種定位方式在室內室外都能用,響應時間較短,耗電較少,但是精度較差。
為了在應用中獲得用戶的信息,你的location provider可以是GPS或者基於網絡,也可以兩者都用。
決定用戶位置面臨的挑戰
從手機上獲取用戶的位置是個比較復雜的問題,無論采取的數據源是什么,總是有一些因素會導致位置信息包含誤差,從而不准確。
誤差來源如下:
多種位置信息源
GPS, Cell-ID, 和Wi-Fi都能為用戶位置提供一定的線索。決定信任和采用哪些數據是一個需要權衡的過程,權衡時要考慮精度、速度、電池效率等。
用戶移動
因為用戶的位置會改變,所以應該考慮每隔一段時間重新估計用戶位置。
變化的精度
每一個位置信息源的精度不是恆定的。也就是說位置估計即便是同一個信息源提供的,它的精確度也是不斷在變化的。
請求位置更新
在Android中獲取位置主要是通過回調函數。
首先通過 LocationManager
的 requestLocationUpdates()
方法注冊監聽器,向其中傳入一個實現了 LocationListener
接口的對象。
你的 LocationListener
對象中必須實現一些回調函數,當用戶位置改變或者當服務狀態改變時,Location Manager就會調用這些回調函數。
比如,下面的代碼就展示了如何定義一個 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()
中的第一個參數指明了location provider的類型,第二個參數是通知的最小時間間隔,第三個參數是通知的最小的改變距離。
如果第二個和第三個參數都設置成0就表示要盡可能頻繁地請求位置通知。
最后一個參數是用戶自己定義的實現了LocationListener接口的對象,它接收位置更新的回調。
如果想要同時利用GPS和網絡定位,可以調用 requestLocationUpdates()
兩次,第一個參數分別是 GPS_PROVIDER
和 NETWORK_PROVIDER
。
權限設置
如果沒有權限設置,程序在請求位置更新時將會失敗。
<manifest ... > <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ... </manifest>
如果用NETWROK_PROVIDER, 那么需要聲明 ACCESS_COARSE_LOCATION和INTERNET;
如果用GPS_PROVIDER, 那么需要聲明 ACCESS_FINE_LOCATION;
ACCESS_FINE_LOCATION是包含了ACCESS_COARSE_LOCATION的,所以兩者都用時可以只聲明ACCESS_FINE_LOCATION。
建立一個最佳性能的模型
為了克服獲取用戶位置時的種種困難,定義一個模型,具體化你的應用如何取得用戶位置。
這個模型包含了你何時開始和何時終止位置更新的監聽,也包含了什么時候使用緩存的位置數據。
獲取用戶位置的流程
獲取用戶位置的典型流程如下:
1.首先開始應用。
2.在某一個點,開始監聽位置更新。
3.維持一個當前位置的“最佳估計”。
4.停止對位置更新的監聽。
5.利用最后一次的最佳位置估計。
這個模型是一個窗口,窗口從開始監聽開始,從停止監聽結束。
決定何時開始監聽更新
可以從應用一開始就啟動監聽,也可以在用戶觸發某個特性后開始。
要清楚如果長時間監聽位置會消耗很多電量,但是短時間的監聽可能達不到足夠的精度。
調用requestLocationUpdates()
開始監聽:
LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER; // Or, use GPS location data: // LocationProvider locationProvider = LocationManager.GPS_PROVIDER; locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
用上次定位的結果獲取快速定位
初次獲得位置信息可能會花費較長時間。在獲得一個比較精確的定位之前,可以利用一個緩存的位置:調用getLastKnownLocation(String)
方法實現。
LocationProvider 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);
維護一個當前最佳估計
因為定位精度是實時變化的,所以很可能最新的位置並不是最准確的。
你應該制定一些標准並包含一套邏輯判斷,來選擇出最佳估計。
這套標准也是隨着使用情景而變化的。
下面是你驗證一個location fix的精確度時可以采取的步驟:
1.檢查是否當期位置數據比之前的估計數據要新很多;
2.檢查當前數據和之前數據的精度,誰更好;
3.檢查當前數據是從哪個provider處獲得的,然后決定是否更加信任它;
一個例子如下:

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和網絡的交互減少,這樣就可以節約電量。但是這樣也就減少了獲得最佳位置估計可以利用的位置數。
降低location provider返回更新的頻率
在窗口中降低更新頻率也可以改善電池效率,但是會導致精度的丟失。requestLocationUpdates()
通過其中兩個參數就可以設定更新的時間和空間間隔,從而設定頻率。
限制provider
根據應用的特定環境或者目標精度等級,可以選擇只利用基於網絡的定位或者只利用GPS定位,而不是同時利用兩者。只與其中的一者交互將減少電量使用,但是會有潛在的精度丟失。
參考資料:
API Guides:Location Strategies
http://developer.android.com/guide/topics/location/strategies.html