不簡單的手機定位

作移動互聯網就不太可能不碰手機端的開發。上週爲了項目須要,俺也挽袖子掄胳膊開始寫起了android程序,還好有java基礎,倒也上手快,寫了幾個小程序,主要都是關於定位方面的。
網上也搜獲得一些相關的文章和教程,但給出的例子效果不太好,並且感受只有其表,卻不明其理。所以寫出此文,分享一些個人經驗。雖然是以android爲主,可是我想對其它平臺的開發也應該有些幫助。
html

這篇文章側重於制定一個合理的定位方案。 java

 

手機定位的方式 
先科普一些基礎知識吧。 android

最簡單的手機定位方式固然是經過GPS模塊(如今大部分的智能機應該都有了)。GPS方式準確度是最高的,可是它的缺點也很是明顯:1,比較耗電;2,絕大部分用戶默認不開啓GPS模塊;3,從GPS模塊啓動到獲取第一次定位數據,可能須要比較長的時間;4,室內幾乎沒法使用。這其中,缺點2,3都是比較致命的。須要指出的是,GPS走的是衛星通訊的通道,在沒有網絡鏈接的狀況下也能用。
另一種常見的定位方式是基站定位。大體思路就是採集到手機上的基站ID號(cellid)和其它的一些信息(MNC,MCC,LAC等等),而後經過網絡訪問一些定位服務,獲取並返回對應的經緯度座標。基站定位的精確度不如GPS,但好處是可以在室內用,只要網絡通暢就行。
還有Wifi定位。和基站定位相似,這種方式是經過獲取當前所用的wifi的一些信息,而後訪問網絡上的定位服務以得到經緯度座標。由於它和基站定位其實都須要使用網絡,因此在Android也統稱爲Network方式。
最後須要解釋一點的是AGPS方式。不少人將它和基站定位混爲一談,但其實AGPS的本質仍然是GPS,只是它會使用基站信息對獲取GPS進行輔助,而後還能對獲取到的GPS結果進行修正,因此AGPS要比傳統的GPS更快,準確度略高。 git

 

Android提供的定位接口 
在寫第一個程序以前,我對android的幻想是這樣的:提供了一個函數,可以讓我直接從GPS模塊中讀取經緯度座標,還有一個函數,可以直接訪問網絡,得到基站定位的結果。因此,我只須要調用調用函數就能夠搞定這一切。
現實和理想老是有很大的差距。Android上的開發徹底不是這麼回事兒。前面提到過,GPS模塊從啓動到獲取數據之間時間會比較長,可能有2~3分鐘時間,因此,若是真有這麼一個函數,那麼你的程序可能會被這個函數阻塞幾分鐘。我想正是基於這樣的考慮,android上要想獲取定位信息,必須使用異步方式。 程序員

代碼大概是這個樣子: 小程序

  1. locManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);  
  2. locListener = new LocationListener() {  
  3.     @Override  
  4.     public void onStatusChanged(String provider, int status,  
  5.             Bundle extras) {  
  6.         // TODO Auto-generated method stub  
  7.     }  
  8.     @Override  
  9.     public void onProviderEnabled(String provider) {  
  10.         // TODO Auto-generated method stub  
  11.     }  
  12.     @Override  
  13.     public void onProviderDisabled(String provider) {  
  14.         // TODO Auto-generated method stub  
  15.     }  
  16.     @Override  
  17.     public void onLocationChanged(Location location) {  
  18.         // TODO Auto-generated method stub  
  19.         mobileLocation = location;  
  20.     }  
  21. };  
  22. locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locListener);  

這是從網上隨便摘一段。簡單解釋一下代碼:
首先,你須要建立一個LocationManager;
而後定義出本身的LocationListener,LocationListener包涵了好幾個成員函數,它們都是回調函數。最重要的一個是「onLocationChanged」,這個函數是在android獲取了新的location信息以後調用的,你能夠在這個函數內來實現本身想要的功能。好比,你能夠定義一個內部location變量,一旦這個函數被調用,就將內部location變量設置成最新的值;
最後,調用LocationManager.requestLocastionUpdates,它實際上是將定義的locationListener註冊到android中。在上面的代碼中,這句話是說讓LocationListener監聽GPS_PROVIDER的變化。GPS_PROVIDER對應於android上的GPS模塊獲取位置信息,還有一個NETWORK_PROVIDER表示經過network方式獲取位置信息。 網絡


問題 異步

那麼接下來就有問題了,何時可以真正得到手機的定位經緯度呢?等着onLocationChanged被調用吧。那它何時會被調用?沒人知道。我寫過一個小程序,測試Network方式下注冊過listener以後(requestLocationUpdates函數)和onLocationChanged被調用之間的時間間隔。測試的網絡條件很好。反覆觀察了幾回,大部分均可以在幾十毫秒內就返回了,但也有一些時候,時間間隔長達幾十秒。這意味着,你的用戶須要等上幾十秒纔能有返回。
因此,第一個須要注意的地方是,不要一直等待你的回調函數onLocationChanged被調用。你須要設置一個timeout機制。
這又會引入第二個問題。若是timeout了,但onLocationChanged仍然沒有返回,怎麼辦?難道只能提示用戶沒法定位嗎?
別急,android還提供了一個函數:getlastKnowLocation。這個函數會返回android平臺最後一次獲取到的位置信息。好比,你能夠這樣: ide

  1. Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);  

因此,即使onLocationChanged沒有被調用,咱們仍然能夠獲取一個位置信息。固然,這裏又引出了第三個問題:這個的返回值值得信賴嗎? 函數

若是用過一些LBS或者地圖程序,你會發現有一個現象:在某些時候你打開地圖結果被定位到的地方是你上一次使用地圖程序的位置。這就是由於程序是採用getLastKnownLocation獲取的位置。這個問題的解決辦法是,須要定義一個標準判斷獲取到的Location是否可信。Android的Location這個類除了包涵有latitude,longitude,還包含有不少其餘的信息,好比什麼時候獲取到的,經過哪一種方式獲取到的,等等。程序員徹底能夠基於這些信息來判斷獲取到的Location是否過期或者是否可信。

 

合理的方案

最後,說一下總體方案。Android的官方文檔【1】給出了推薦的方案:

 
首先註冊本身的LocationListener,讓它同時監聽GPS_PROVIDER和NETWORK_PROVIDER;
而後能夠調用getLastKnownLocation得到一個Location值,這個值能夠做爲一個備選值;
而後在一段用戶可接受的時間內,不斷接收從onLocationChanged返回的位置,並同以前的值作比較,選取其中的最佳;
最後,會剩下一個篩選後的最優結果,你須要判斷這個結果是否可接受。若是能夠接受,返回給用戶,若是不行,告訴用戶沒法定位。
整個過程你須要定義兩個重要的函數:一個是比較兩個Location信息,返回其中好的那個;另外一個函數則用來判斷Location信息是否能夠被接受。

 

Reference:

【1】http://developer.android.com/guide/topics/location/obtaining-user-location.html

相關文章
相關標籤/搜索