最簡單的手機定 位方式當然是通過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更快,準確度略高。
Android提供的定位介面
在寫第一個程式之前,我對Android的幻想是這樣的:提供了一個函數,能夠讓我直接從GPS模組中讀取經緯度座標,還有一個函數,能夠直接存取網路,獲得基站定位的結果。所以,我只需要調用調用函數就可以搞定這一切。
現實和理想總是有很大的差距。Android上的開發完全不是這麼回事兒。前面提到過,GPS模組從啟動到獲取資料之間時間會比較長,可能有2~3分鐘時 間,所以,如果真有這麼一個函數,那麼你的程式可能會被這個函數阻塞幾分鐘。我想正是基於這樣的考慮,android上要想獲取定位資訊,必須使用非同步方 式。
代碼大概是這個樣子:
locManager = (LocationManager) this.getSystemService(CoNtext.LOCATION_SERVICE);

locListener = new LocationListener() {

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
TODO Auto-generated method stub
}


@Override
public void onProviderEnabled(String provider) {
TODO Auto-generated method stub
}

@Override
public void onProviderDisabled(String provider) {
TODO Auto-generated method stub
}

@Override
public void onLocationChanged(Location location) {
TODO Auto-generated method stub
mobileLocation = location;
}

};

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平臺最後一次獲取到的位置資訊。比如,你可以這樣:
Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
所以,即便onLocationChanged沒有被調用,我們仍然可以獲取一個位置資訊。當然,這裡又引出了第三個問題:這個的傳回值值得信賴嗎?
如果用過一些LBS或者地圖程式,你會發現有一個現象:在某些時候你打開地圖結果被定位到的地方是你上一次使用地圖程式的位置。這就是因為程式是采 用getLastKnownLocation獲取的位置。這個問題的解決辦法是,需要定義一個標準判斷獲取到的Location是否可信。Android 的Location這個類除了包涵有latitude,longitude,還包含有很多其他的資訊,比如何時獲取到的,通過哪種方式獲取到的,等等。程 序員完全可以基於這些資訊來判斷獲取到的Location是否過時或者是否可信。

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

20130214064849169  

首先註冊自己的LocationListener,讓它同時監聽GPS_PROVIDER和NETWORK_PROVIDER;
然後可以調用getLastKnownLocation獲得一個Location值,這個值可以作為一個備選值;
然後在一段使用者可接受的時間內,不斷接收從onLocationChanged返回的位置,並同之前的值做比較,選取其中的最佳;
最後,會剩下一個篩選後的最優結果,你需要判斷這個結果是否可接受。如果可以接受,返回給使用者,如果不行,告訴使用者無法定位。
整個過程你需要定義兩個重要的函數:一個是比較兩個Location資訊,返回其中好的那個;另一個函數則用來判斷Location資訊是否可以被接受。

創作者介紹
創作者 shadow 的頭像
shadow

資訊園

shadow 發表在 痞客邦 留言(0) 人氣()