1. 程式人生 > >Android 位置服務與GPS實時定位

Android 位置服務與GPS實時定位

一、概述

據專家說:人類活動80%的資訊與空間位置資訊有關,這還真不是專家吹牛。舉個例子:你跟女神約飯,你得知道女神在哪裡吧?你們選擇約在哪裡的餐廳最合適?你們過去分別有多遠?你們怎麼過去?——總結起來就是在哪裡、有多遠、怎麼去?——技術翻譯:位置 導航。

手機等移動裝置是非常好的工具,都集成了定位模組,為使用者提供位置服務。日常生活中用的比較多的:

高德、百度、騰訊、Google等手機電子地圖;

美團、大眾點評、滴滴、快的、uber、去哪兒、攜程等O2O、生活服務;

微信、QQ、陌陌等社交工具的附近的人、位置共享等;

最近火起來的悅跑圈、咕咚等戶外運動工具;

京東、淘寶等電商的物流配送,總是從最近的倉庫發貨,節省物流成本;

無人機、無人駕駛汽車等;

……

這些都統稱為基於位置的服務(LBS)。

對於普通使用者來說,android裝置提供了: GPS,網路(Cell-ID和Wifi)兩種方式,可以在手機中設定定位方式。作為開發者則需要更深入理解android的位置服務,在android的location包中有一個LocationManager類中有四個靜態變數:GPS_PROVIDER、NETWORK_PROVIDER、PASSIVE_PROVIDER、FUSED_PROVIDER提供不同的定位模式。

這幾種定位方式,各有優缺點:

(1)、GPS(GPS、AGPS):衛星定位模式、定位精度最高(10米)、能夠獲取海拔高度、耗電量大、室內無法定位。

採用的是美國GPS衛星的民用訊號,最少需4顆衛星來實現精確定位,目前正常情況的定位精度能夠達到10米內,採用特殊的技術方法和晶片能夠達到米級、釐米級的精度,一般專業裝置才有這需求,如測繪、地質、工程建設等行業,工業級GPS晶片要比手機晶片貴N倍。在建築密集遮擋、大面積水域、電磁干擾等區域,定位精度會大幅下降。

AGPS定位模式:GPS的初始定位可能需要點時間,幾十秒、幾分鐘甚至十幾分鍾,不少手機提供AGPS定位模式(手機需要開啟網路連線):首先根據網路的快速定位來獲得一個大致位置,然後根據這個位置去幫助GPS模組快速地搜尋到附近的衛星,能夠有效改善GPS初始定位時間長的問題。室外定位精度與GPS模式相當。有些(不是所有)運營商也會在沒有GPS訊號的時候,如室內,通過伺服器中的GPS衛星資訊和網路實現低精度定位。

美國的GPS、我國北斗衛星導航系統(BeiDou Navigation Satellite System,BDS)、歐洲的伽利略GALILEO、俄羅斯格洛納斯衛星導航系統(GLONASS)是聯合國衛星導航委員會已認定的GNSS(Global Navigation Satellite System)供應商,GPS使用最廣泛;我國的北斗導航發展最快,軍事、政府等專業部門已經廣泛使用,華為等已經推出了北斗手機,在消費電子領域北斗導航必定前途無量。

開發過程中需在AndroidMenifest.xml檔案中配置許可權:

android.permission.ACCESS_FINE_LOCATION

(2)、網路(CellID, WiFi MACID):網路定位模式(基站、wifi網路)、精度較低(1000米)、無海拔高度、初始定位速度快、耗電低。基於基站和wifi熱點進行位置定位,這種定位方式精度取決於伺服器,即取決於將基站或WIF節點資訊翻譯成位置資訊的伺服器的能力。

開發過程中需在AndroidMenifest.xml檔案中配置許可權:

android.permission.ACCESS_COARSE_LOCATION

 (3)、passive(CellID, WiFi MACID):被動定位方式,精度取決於定位資訊提供方。比較明顯,就是用現成的,不主動請求位置資訊,當其他應用使用定位更新了定位資訊,系統會儲存下來,該應用接收到訊息後直接讀取就可以了。比如如果系統中已經安裝了百度地圖,高德地圖。

開發過程中需在AndroidMenifest.xml檔案中配置許可權:

android.permission.ACCESS_FINE_LOCATION

(4)、fusedlocation(融合定位服務):Google api,API容易使用、定位精度高、根據電量自動選擇定位模式、Google的融合定位服務天朝不能用,最新訊息稱阿里巴巴(高德地圖)與華為正在聯合研發融合定位服務。不需要明確指定定位模式,由服務提供者自動判斷,給出最佳的定位方式,需要Google API,Google Play Services。

開發過程中需在AndroidMenifest.xml檔案中配置許可權:

android.permission.ACCESS_FINE_LOCATION

老外做的一個比較,裡面有一些錯誤,別都當真。可能是不同的地區、不同的手機品牌的差異。

精度

耗電

Technology

20ft6m

High

Autonomous GPS, Provider: gps

1.uses GPS chip on the device

2.line of sight to the satellites

3.need about 7 to get a fix

4.takes a long time to get a fix

5.doesn’t work around tall buildings

200ft

60m

Medium – Low

Assisted GPS (AGPS), Provider: network

6.uses GPS chip on device, as well as assistance from the network (cellular network) to provide a fast initial fix

7.very low power consumption

8.very accurate

9.works without any line of sight to the sky

10.depends on carrier and phone supporting this (even if phone supports it, and network does not then this does not work)

5300ft / 1mile

1.6km

Low

CellID lookup/WiFi MACID lookup, Provider: network or passive

1.very fast lock, and does not require GPS chip on device to be active

2.requires no extra power at all

3.has very low accuracy; sometimes can have better accuracy in populated and well mapped areas that have a lot of WiFi access points, and people who share their location with Google

二、開發方式

在開發的過程中有幾種方式可以選擇:

(1)、使用android原生API開發:相對靈活,不依賴服務提供商,開發工作量相對較大。學習android的位置服務則從這裡入手能夠比較深入。

(2)、使用百度地圖、高德地圖、騰訊地圖、Google地圖等API:位置服務提供商對android的定位服務進行了封裝和擴充套件,將定位和地圖進行了結合,功能強大,定位、導航、高階分析都可以實現,可以快速搭建業務系統。需要服務商的開發祕鑰才能使用,同時一些服務商會左右一些限制,當系統訪問量比較大的時候可能要收費。也可以使用一些開源地圖的android版API。

3)、解析GPS導航電文:android手機提供的GPS模組一般都是封裝好的,可以直接使用獲取GPS三維座標資訊,在一些移動應用中可能需要對GPS座標進行更加複雜的應用,如無人機、微控制器、差分GPS等,則需要自行解析GPS的導航電文,GPS晶片一般都採用國際通用的NMEA 0183電文格式(美國國家海洋電子協會弄的),

資料格式為:$資訊型別,xxx,xxx,xxx,xxx,xxx,xxx,xxx,

資訊型別為:GPGSV:可見衛星資訊、GPGLL:地理定位資訊、GPRMC:推薦最小定位資訊、GPVTG:地面速度資訊、GPGGA:GPS定位資訊、GPGSA:當前衛星資訊、……。一般情況,我們使用推薦的定位座標GPRMC中的資料資訊,獲取三維座標。

GPRMC:

推薦最小資料量的GPS資訊(Recommended Minimum Specific GPS/TRANSIT Data)

$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*<13><CR><LF>

<1> UTC(Coordinated Universal Time)時間,hhmmss(時分秒)格式

<2> 定位狀態,A=有效定位,V=無效定位

<3> Latitude,緯度ddmm.mmmm(度分)格式(前導位數不足則補0)

<4> 緯度半球N(北半球)或S(南半球)

<5> Longitude,經度dddmm.mmmm(度分)格式(前導位數不足則補0)

<6> 經度半球E(東經)或W(西經)

<7> 地面速率(000.0~999.9節,Knot,前導位數不足則補0)

<8> 地面航向(000.0~359.9度,以真北為基準,前導位數不足則補0)

<9> UTC日期,ddmmyy 格式

<10> Magnetic Variation,磁偏角(000.0~180.0度,前導位數不足則補0)

<11> Declination,磁偏角方向,E(東)或W(西)

<12> Mode Indicator,模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)

<13> 校驗和。

三、Android位置相關類庫

建議閱讀android關於位置服務相關的原始碼。

1)、LocationManager:位置服務管理器類

是獲取位置資訊的入口級類,要獲取位置資訊,首先需要獲取一個LocationManger物件:LocationManager  pLocationManager = (LocationManager) Context.getSystemService(Context.LOCATION_SERVICE);

2)、LocationProvider:位置源提供者

用於描述位置提供者資訊,可以先使用方法獲取最佳提供者的名稱:String providerName = LocationManger.getBestProvider(Criteria criteria, boolean enabledOnly);LocationManger.getProvider(String name)獲取LocationProvider物件。

3)、LocationListener:位置監聽介面

用於監聽位置(包括GPS、網路、基站等所有提供位置的)變化,監聽裝置開關與狀態。實時動態獲取位置資訊,首先要實現該介面,在相關方法中新增實現功能的程式碼,實現該介面可以使用內部類或者匿名實現,java的基礎知識。然後註冊監聽:LocationManger.requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)。使用完之後需要在適當的位置移除監聽:.removeUpdates( listener)。LocationListener需要實現的方法:

onLocationChanged(Location location);當位置發生變化的時候會自動呼叫該方法,引數location記錄了最新的位置資訊。

onStatusChanged(String provider, int status, Bundle extras);當位置提供者的狀態發生改變(可用到不可用、不可用到可用)時自動呼叫該方法;引數provider為位置提供者的名稱,status為狀態資訊:OUT_OF_SERVICE 、AVAILABLE 、TEMPORARILY_UNAVAILABLE ,extras為附加資料:key/value,如satellites

onProviderEnabled(String provider);位置資訊提供者可用時自動呼叫,比如使用者關閉了GPS時,provider則為“gps”

onProviderDisabled(String provider);位置資訊不可用時自動呼叫

4)、GpsStatus.ListenerGPS狀態監聽的一個介面

使用方法與locationListener介面類似,先實現介面並建立物件,實現介面中的方法:onGpsStatusChanged(int event);在方法中實現對衛星狀態資訊變化的監聽,根據event的型別編寫邏輯程式碼。建立物件後再註冊監聽:.addGpsStatusListener(GpsStatus.Listener listener);使用後在合適的位置釋放監聽:.removeGpsStatusListener(GpsStatus.Listener listener)

5)、GpsStatus.NmeaListener:監聽GPSNMEA 0183資料

這也是一個介面,用於監聽串列埠上的GPS資料,標準的NMEA 0183格式導航電文。不過在實際android開發中基本不使用該介面物件,採用常規的方法也獲取不到串列埠上的資料,具體原因不詳。實現該介面需要實現其個方法:onNmeaReceived(long timestamp, String nmea); 然後新增監聽:.addNmeaListener( listener);用完後在合適的位置釋放監聽:LocationManager.removeNmeaListener(GpsStatus.NmeaListener listener)

6)、Location:位置物件

描述地理位置資訊的類,記錄了經緯度、海拔高度、獲取座標時間、速度、方位等。可以通過LocationManager.getLastKnowLocation(provider)獲取位置座標,provider就是上文中提到的GPS_PROVIDER、NETWORK_PROVIDER、PASSIVE_PROVIDER、FUSED_PROVIDER;不過很多時候得到的Location物件為null;實時動態座標可以在監聽器locationListener的onLocationChanged(Location location)方法中來獲取。

7)、Criteria:用於選擇位置資訊提供者的輔助類

建立LocationProvider物件時會使用到該類,上文中內容。定位資訊提供者會根據精度、電量、是否提供高度、速度、方位、服務商付費等資訊進行排序選擇定位提供者。 一個示例:

   /** this criteria needs high accuracy, high power, and cost */

   public static Criteria createFineCriteria() {

      Criteria c = new Criteria();

      c.setAccuracy(Criteria.ACCURACY_FINE);//高精度

      c.setAltitudeRequired(true);//包含高度資訊

      c.setBearingRequired(true);//包含方位資訊

      c.setSpeedRequired(true);//包含速度資訊

      c.setCostAllowed(true);//允許付費

      c.setPowerRequirement(Criteria.POWER_HIGH);//高耗電

      return c;

   }

8)、GpsStatus:用於描述GPS當前狀態的類

GpsStatus物件中的方法:getMaxSatellites()獲取預設最多衛星數255,getTimeToFirstFix()用於獲取首次定位時間,Iterable<GpsSatellite> getSatellites()用於獲取衛星狀態資訊的列表,返回的是一個列舉物件。有幾個用於描述GPS狀態變化型別的靜態變數:GPS_EVENT_STARTED、GPS_EVENT_STOPPED、GPS_EVENT_FIRST_FIX、GPS_EVENT_SATELLITE_STATUS。在GpsStatus.Listener 的onGpsStatusChanged回撥方法中會使用到這幾個靜態變數判斷衛星狀態變化型別。可以使用GpsStatus gpsStatus = locationManager.getGpsStatus(null)獲取最新的衛星狀態資訊,按照android原始碼中的解釋getGpsStatus一般只在onGpsStatusChanged方法中使用。

9)、GpsSatellite:衛星物件

用於描述單顆衛星的狀態資訊,包括衛星的方位、高度、偽隨機噪聲碼、信噪比等資訊。GpsStatus、GpsSatellite、GpsStatus.Listener會結合在一起使用。使用方法獲取衛星列表列舉,並轉換為一個ArrayList後使用:

Iterable<GpsSatellite> iterable=gpsStatus.getSatellites();

Iterator<GpsSatellite> itrator=iterable.iterator();

ArrayList<GpsSatellite> satelliteList=new ArrayList<GpsSatellite>();

然後就可以遍歷每一顆衛星了:

while (itrator.hasNext() && count <= maxSatellites) {       

      GpsSatellite satellite = itrator.next();       

      count++;     

}

四、位置服務開發流程

上面的內容有點冗長,那麼在開發流程是什麼樣的呢?動態監聽位置資訊的大致流程:

(1)    配置許可權:   AndroidManifest.xml中配置許可權: <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

(2)    獲取LocationManager 型別物件: mLocationManager = (LocationManager) pContext.getSystemService (Context.LOCATION_SERVICE); pContext為Context型別的物件

(3)    獲取最佳位置定位方式pProvider:mLocationManager.getBestProvider(pCriteria, true); pCriteria為Criteria型別的物件,包含精度、是否返回高度、方位、速度等資訊。建立Criteria物件示例:

/** this criteria needs high accuracy, high power, and cost */

   public static Criteria createFineCriteria() {

      Criteria c = new Criteria();

      c.setAccuracy(Criteria.ACCURACY_FINE);//高精度

      c.setAltitudeRequired(true);//包含高度資訊

      c.setBearingRequired(true);//包含方位資訊

      c.setSpeedRequired(true);//包含速度資訊

      c.setCostAllowed(true);//允許付費

      c.setPowerRequirement(Criteria.POWER_HIGH);//高耗電

      return c;

   }

(4)    實現LocationListener介面:可以採用內部類(MyLocationListener)或匿名類方式實現,重寫介面方法,根據需要新增相應的程式碼:onLocationChanged(Location location);onStatusChanged(String provider, int status, Bundle extras);onProviderEnabled(String provider);onProviderDisabled(String provider);

(5)    建立MyLocationListener物件mLocationListener,並新增監聽:mLocationListener = newMyLocationListener(); mLocationManager.requestLocationUpdates(pProvider, MIN_TIME_UPDATE, MIN_DISTANCE_UPDATE, mLocationListener);

(6)   使用完釋放監聽:mLocationManager.removeUpdates(mLocationListener);該方法執行的位置需要特別注意,如果是在Activity物件中,根據Activity的生命週期,onPause方法中比較合適,因為onStop、onDestroy兩個方法在異常情況下不會被執行。

(7)    如果需要監聽GPS衛星狀態,則需要實現GpsStatus.Listener介面,並建立物件、新增監聽、使用完後釋放監聽:實現介面private class MyGpsStatusListener implements GpsStatus.Listener;建立物件mGpsStatusListener = new MyGpsStatusListener();新增監聽isGpsStatusListener = mLocationManager.addGpsStatusListener (mGpsStatusListener);使用完後釋放監聽mLocationManager.removeGpsStatusListener(mGpsStatusListener);

五、可複用的GPS工具類

在開發的過程中,可以直接在activity中實現獲取位置資訊的功能,開發LBS程式。一個新的需求:需要多個activity用到實時獲取的位置資訊。可以在多個activity中編寫重複的位置服務相關的程式碼邏輯也就是第四部分中的內容,重複的程式碼非常多,高效的開發者絕對不會做這麼SB的事情。那麼可以編寫一個工具類classLocationUtils,將公共的功能邏輯封裝好,在activity中實現差異化的內容。

等等,好像多個activity中動態獲取位置資訊,工具類實現有點棘手,使用計時器?可以但不是最好的方式,java基礎比較好的都知道java面向介面的程式設計,可以完美的解決該問題。可以定義一個介面,activity實現該介面即可。

(1)定義介面:

public interface LocationHelper{

      void UpdateLocation(Location location);//位置資訊發生改變

      void UpdateStatus(String provider, int status, Bundle extras);//位置狀態發生改變

      void UpdateGPSStatus(GpsStatus pGpsStatus);//GPS狀態發生改變

   }

(2)實現介面:java多型

public class LocationActivity extends Activity implements LocationHelper {

   @Override

   protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      setContentView(R.layout.activity_location);

   }

   @Override

   public void UpdateLocation(Location location) {

      // TODO Auto-generated method stub

   }

   @Override

   public void UpdateStatus(String provider, int status, Bundle extras) {

      // TODO Auto-generated method stub

   }

   @Override

   public void UpdateGPSStatus(GpsStatus pGpsStatus) {

      // TODO Auto-generated method stub

   }

}

(3)工具類LocationUtils中呼叫實現了介面LocationHelper的物件

建構函式:

無引數建構函式定義為私有的private LocationUtils(),這樣可以確保使用者在使用的時候只能呼叫帶引數LocationHelper的建構函式:Public  LocationUtils (LocationHelper pInterface)

在上述監聽器 LocationListener、GpsStatus.Listener方法中呼叫LocationHelper的方法,這樣可以做到無論是在哪個activity中,都可以實現動態更新位置座標資訊。

//實現動態更新位置座標資訊、狀態資訊

   private class MyLocationListener implements LocationListener {

      @Override

      public void onLocationChanged(Location location) {

         // TODO呼叫介面的方法,java多型

         mLocationHelper.UpdateLocation (location);

      }

      @Override

      public void onStatusChanged(String provider, int status, Bundle extras) {

         // TODO 呼叫介面的方法,

mLocationHelper. UpdateStatus (provider,status,extras);

      }

      @Override

      public void onProviderEnabled(String provider) {

         // TODO Auto-generated method stub

      }

      @Override

      public void onProviderDisabled(String provider) {

         // TODO Auto-generated method stub

      }

   }

//實現動態更新GPS衛星狀態資訊

   private class MyGpsStatusListener implements GpsStatus.Listener {

      @Override

      public void onGpsStatusChanged(int event) {

         // TODO 呼叫介面方法。

      GpsStatus pGpsStatus = mLocationManager.getGpsStatus (null);

         mLocationHelper.UpdateGPSStatus(pGpsStatus);

   }

//初始化監聽

public boolean initLocationListener(Context pContext){

}

//移除監聽

public void RemoveLocationListener(){

}

(3)Activity中呼叫工具類

@Override

protected void onResume() {

   // TODO Auto-generated method stub

super.onResume();

mLocationUtils = new LocationUtils(this);//建立工具類物件

mSucceed = mLocationUtils.initLocationListener(this);//呼叫工具類中的初始化方法。

}

@Override

protected void onPause() {

super.onPause();

if (mSucceed == true) {

mLocationUtils.RemoveLocationListener();//使用完後移除監聽

}

}