GPS定位詳解——涉及GPS版本變化、定位獲取失敗等常見問題。

GPS詳解

GPS本身並不複雜,但是因爲GPS本身定位比網絡還慢的原因用好GPS還是需要費點事的。

1. GPS相關類說明(android.location包)

1.1. 主要必須涉及到的類

  • LocationManager 用於發起定位請求
  • LocationListener 用於監聽定位信息的有關更新(包括位置變化、相關定位設備狀態改變或用戶打開關閉相關定位設備)
  • Location 定位信息
  • Criteria 幫助開發者選取最好用的定位設備

採用GPS定位時往往需要監聽衛星狀態,根據衛星狀態去決定是否繼續採用GPS定位還是採用其他替補定位方式。在android的location包中同樣提供了相關功能,但是在API 24以上定位相關包結構有所更改。

1.2. API 23及以下版本

  • GpsStatus 所有搜索到的衛星狀態信息,其中有一個GpsStatus.Listener用於監聽衛星狀態的改變。
  • GpsSatellite 單個的衛星信息,包括衛星的方位、高度、僞隨機噪聲碼、信噪比等信息,具體的可進入源碼查看。

1.3. API 24及以上版本

  • GnssStatus 相當於將GpsStatus以及GpsSatellite整合在了一起

API 24只是對定位接口進行了一些接口上的改進,除了使用起來更加方便以外沒有什麼其他優勢。

1.4. 不需要用到的類

  • Address、Geocoder、GnssClock、GnssMeasurement、GnssMeasurementsEvent、GnssNavigationMessage、SettingInjectorService、LocationProvider這些class 用於通過經緯度獲取地理信息以及自定義一些定位實現等,只有定位需求的問題並不需要這些。

2. GPS相關基礎知識說明

需要了解,在設置界面或者下拉菜單中的GPS按鈕,僅僅是一個GPS的總開關而已,打開它GPS並不會開始定位,只是打開了GPS的訪問權限,使具有權限的APP可以去請求GPS進行定位,真正GPS開始工作以及停止工作需要應用程序去控制。

國測局座標(火星座標,GCJ02)。國內出版的各種地圖系統(包括電子形式),必須至少採用GCJ-02對地理位置進行首次加密,而在國內正常銷售、使用的GPS芯片獲取的定位信息也必須通過GCJ-02進行處理,處理成加密後的座標。這將導致國外未採用GCJ02座標系統的地圖在採用了GCJ座標系統的GPS上不能正常使用,反之亦然。

LocationManager中包含有幾種定位模式,NETWORK_PROVIDER、GPS_PROVIDER、PASSIVE_PROVIDER這幾種(API 28中又提供了一種WIFI模式),但在當前常見手機中可用的只有兩個GPS_PROVIDER已經PASSIVE_PROVIDER。需要注意的是帶GPS的Android設備一般肯定支持GPS,但是可以訪問網絡的手機卻並不一定提供NETWORK_PROVIDER的支持(雖然系統可能不支持網絡定位,但我們的程序卻可以自己去實現基站定位)。

不要相信國內系統的LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))的返回值,國內被改過的系統中isProviderEnabled用於判斷GPS還是可以的,判斷其他定位方式就算了。

3. GPS定位相關方法(以API 23及以下版本爲例)

Android GPS定位主要通過LocationManager類來實現,其中提供了很多方法,大體上可以分爲這麼幾類方法:

3.1. Provider相關方法

  • createProvider
  • getAllProviders
  • getProviders
  • getProvider
  • getBestProvider

這幾個方法用於獲取位置提供商,但是在國內沒有實際用處,只需要GPS即可。

3.2. 請求定位相關方法(只看方法名,忽略具體參數)

  • requestLocationUpdates (開始GPS定位,GPS開始工作)
  • requestSingleUpdate (開始GPS定位,GPS開始工作)
  • removeUpdates (停止GPS定位,GPS有可能會停止工作,因爲有可能其他的APP也啓用了GPS)

這幾個方法開始請求GPS定位,用於獲取位置信息。

3.3. 添加GPS狀態監聽

  • addGpsStatusListener
  • removeGpsStatusListener

GPS定位過程中,衛星會處於不斷變化中,這幾個方法可以註冊衛星狀態監聽器用於實時監測衛星狀態的變化。

4. GPS定位步驟

4.1. Android中 GPS定位極爲簡單,首先拿到LocationManager對象:

LocationManager mLocationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);

4.2. 創建定位成功的監聽器

LocationListner listener = new LocationListener() {

    @Override
    public void onLocationChanged(Location location) {
        // 收到位置信息(onLocationChanged 不要糾結於方法名,下面會明白)
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // 定位提供程序狀態發生改變(國內只考慮GPS,而GPS一般不會回調這個方法)
    }

    @Override
    public void onProviderEnabled(String provider) {
        // GPS打開
    }

    @Override
    public void onProviderDisabled(String provider) {
        // GPS 關閉
    }

};

4.3. 將定位監聽器註冊到LocationManager上去

上面提到 請求定位相關方法,這些以request開頭的方法,可以將我們寫好的定位監聽註冊到系統的定位進程上(其實是註冊在了你的LocationManager對象上,而LocationManager對象中一個內部類對象註冊在系統進程上,IPC相關),系統定位進程收到定位請求開始根據具體參數執行定位請求,當系統進程成功獲取到定位信息後回去遍歷通知所有註冊過來的位置監聽器(LocationListener)。當然,如果你的設備只支持GPS,你定位請求使用NETWORK_PROVIDER正常情況下是不會通知你的位置監聽器的。具體看下request相關方法:

如在 請求定位相關方法 中看到的,定位相關方法主要有兩類,分別是requestLocationUpdates、requestSingleUpdate。其中requestLocationUpdates方法用於表達不斷地獲取位置信息,具體的看其中一個

public void requestLocationUpdates(String provider, long minTime, float minDistance,LocationListener listener)

該方法第一個參數表示定位設備類型(國內手機直接使用GPS_PROVIDER),第二個表示多長時間纔回調一次LocationListener,第三個參數表示距離上一次定位位置超過多遠纔回調一次LocationListener。

需要注意的是,上面的方法看似很完美,其實卻存在很大缺陷,原因有三:

  • GPS定位是一個緩慢的過程。GPS冷啓動一般需要40s左右才能收到定位回調,熱啓動定位還算較快一般數秒內可以定位成功。
  • 而且在室內沒有GPS信號,意味着在室內是不可能通過GPS定位成功的
  • 即使是在室外,也受到你所處的環境的限制,空曠的地方定位速度比高樓林立的地方要準確很多快很多。

而你註冊的LocationListener只有等到你定位成功的時候他的onLocationChange(Location )方法纔會被回調。也就是說,很多時候,你在requestLocationUpdates中設置了minTime、minDistance會發現實際效果並不好,這個時候不要奇怪很正常。

你會發現,在重載的幾個requestLocationUpdates方法中,有時候會有一個Criteria或者Looper對象,這兩個對象都是給開發者提供一些便利的工具。

  • 其中Criteria對象是幫助你在不同Android設備上選擇不同的定位設備,通過Criteria中參數的配置,會選擇一個最適合的定位設備進行定位。但是在國內就沒必要了,直接用GPS就行,你沒有其他選擇。
  • Looper對象是幫助你在你指定的線程中回調你註冊的LocationListener對象中的各個方法。

request相關方法中還有一類requestSingleUpdate,該類requestSingle方法表示進行單次定位,即定位成功以後便會自動解綁掉註冊在系統進程的LocationManager。

需要注意的是,不管是上面requestLocationUpdates方法或者是requestSingleUpdate方法,從該方法被調用開始直至成功獲取到定位信息,這段時間內GPS芯片將處於活躍狀態,而活躍的GPS芯片會影響到系統休眠,導致系統不能進入休眠狀態,造成耗電。具體點說,調用requestLocationUpdates將導致GPS一直處於活躍狀態,而調用requestSingleUpdate方法會導致第一次回調onLocationChange之前GPS芯片一直處於活躍狀態。所以,在某些狀況下設備必須具備主動停掉GPS的機制,當然這種機制要使用LocationManager的removeUpdates去實現。

5. 衛星狀態監聽,並根據衛星狀態判斷是否繼續定位

上面提到,爲了節省Android設備的電量,啓動GPS定位後某些情況下必須要關掉GPS。如果GPS正常定位,數秒內定位成功,這種情況下很好處理,我們採用requestSingleUpdate方法實現單次定位即可,但是如果處於室內,沒有GPS信號的情況下我們將長時間處於定位狀態,電池電量將很快耗盡。而對於requestLocationUpdates方法更是需要在某種情況下去停掉GPS。

我們最重要的任務就是要找到關掉GPS的時機。基於上面分析可以找到這個時機,就是,當我們確定GPS在這種情況下永遠都不能成功定位時,就需要關閉掉GPS。但是怎麼確認GPS永遠也定不到位呢?這就需要去通過GPS芯片收到的GPS衛星信號去做出判斷。於是就引出了我們對於GPS衛星狀態的監聽。

5.1. 創建衛星狀態監聽器

GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {
    @Override
    public void onGpsStatusChanged(int event) {
        // GPS 用於報告GPS的狀態變化,包含以下四種狀態:
        // GpsStatus.GPS_EVENT_STARTED      開始GPS定位,表示GPS開始定位,注意和LocationListener中的onProviderEnabled進行區分
        // GpsStatus.GPS_EVENT_STOPPED      停止GPS定位,注意和LocationListener中的onProviderDisabled進行區分
        // GpsStatus.GPS_EVENT_FIRST_FIX    表示GPS自啓動以來首次定位成功
        // GpsStatus.GPS_EVENT_SATELLITE_STATUS GPS衛星狀態改變(定位過程中GPS的衛星狀態一直處於變化中的,該方法會不斷地進行回調)
    }
};

5.2. 監聽衛星狀態

GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {
    @Override
    public void onGpsStatusChanged(int event) {
        if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
            // 需要通過LocationManager拿到具體的衛星狀態
            GpsStatus gpsStatus = locationManager.getGpsStatus(null);
            // 然後獲取衛星狀態的迭代器
            Iterator<GpsSatellite> iterator = gpsStatus.getSatellites().iterator();
            while (iterator.hasNext()) {
                GpsSatellite satellite = iterator.next();
                // 該方法獲取到一個衛星的信噪比(信號噪聲比),信噪比越大表示信號越強。
                // 通過GpsSatellite對象你可以獲取到很多信息,可以直接查看GpsSatellite對象。
                float nr = satellite.getSnr();
            }
        }
    }
};

有了衛星的信噪比就可以根據信噪比的好壞去判斷是否能定位成功了,正常情況下信噪比能達到30就達到了及格線,但是需要有三顆以上衛星同時及格纔可以定位成功。但是測試發現,信噪比在27左右的時候也是能定位成功的只是定位時間較長。還有一點需要注意,你或許會奇怪GPS衛星明明只有14顆,爲什麼我收到的衛星個數甚至多於24,搞硬件的告訴我的答案是,現在的GPS天線、芯片不僅僅回去檢查GPS衛星獲取定位,而是直接集成了幾種定位系統,隨便觀測到什麼只要達標就能定位成功(聽着意思是各衛星之間也是相互兼容的,不需要糾結,可以確認的是達標後確實能定位成功)。而且你回發現其中有一個值最大,一般是位於你頭頂的那顆,如果在你頭頂沒有遮蓋物的情況下位於你頭頂的那顆信號都不行,那就信號真的很差了。

可以定位的標準爲,三顆以上信噪比達到25以上。

5.3. 基於衛星狀態的定位實現

於是,我們在GpsStatus.Listener中去判斷衛星狀態,判斷達到25以上的衛星是否有3顆,有的話就進行定位,否則停止定位。代碼很簡單如下:

// 1. 拿到LocationManager對象
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

// 2. 創建定位監聽
locationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        // 定位成功回調
    }
    ...(省略其他三個方法)
};

// 3. 創建衛星狀態監聽
GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {
    @Override
    public void onGpsStatusChanged(int event) {
        if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
            // 計算達標衛星個數
            int validSatelliteCount = 0;
            GpsStatus gpsStatus = locationManager.getGpsStatus(null);
            Iterator<GpsSatellite> iterator = gpsStatus.getSatellites().iterator();
            while (iterator.hasNext()) {
                GpsSatellite satellite = iterator.next();
                float nr = satellite.getSnr();
                if (nr > 25) {
                    validSatelliteCount++;
                }
            }
            if (validSatelliteCount < 3) {
                // 不達標取消定位
                locationManager.removeUpdates(locationListener);
            }
        }
    }
};

// 添加衛星監聽開始定位
locationManager.addGpsStatusListener(gpsStatusListener);
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, locationListener, getMainLooper());

6. GPS定位方案改進版

梳理了GPS的定位流程以及相關要點,實現了一個簡單版的GPS定位實現。但是這樣依然不完美,GPS定位方案依然需要改進。
假設GPS需要每五分鐘定位一次,使用requestLocationUpdates方法時間參數設置爲5min對於普通移動設備來說顯然不合適,定位失敗的情況下電量消耗會過大。所以呢,我們定位方案改進如下:

考慮GPS熱啓動的話一般5s內可以定位成功(GPS 芯片特別差就另當別論),所以我們爲GPS定位過程加上一個超時時間5s,GPS定位開始後,5s內定位成功就成功返回並關掉GPS定位,出現失敗的時候,我們應該去計算剛纔5s內每次衛星狀態改變時是否都達到了定位標準。如果5s內有30%以上的概率達到了定位標準很可能GPS處於冷啓動狀態,或者GPS設備偶爾處於有遮蓋物的地方,則延長定位時間直至定位成功或者衛星狀態達標概率低於30%。

7. Blog 說明

  • 比較好的GPS定位方案
  • GPS沒有收到定位回調 因爲GPS定位很慢,並且室內信號很弱
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章