據專家說:人類活動80%的信息與空間位置信息有關,這還真不是專家吹牛。舉個例子:你跟女神約飯,你得知道女神在哪裏吧?大家選擇約在哪裏的餐廳最合適?大家過去分別有多遠?大家怎麼過去?——總結起來就是在哪裏、有多遠、怎麼去?——技術翻譯:位置 + 導航。 java
手機等移動設備是很是好的工具,都集成了定位模塊,爲用戶提供位置服務。平常生活中用的比較多的: android
高德、百度、騰訊、Google等手機電子地圖; git
美團、大衆點評、滴滴、快的、uber、去哪兒、攜程等O2O、生活服務; 編程
微信、QQ、陌陌等社交工具的附近的人、位置共享等; api
最近火起來的悅跑圈、咕咚等戶外運動工具; 服務器
京東、淘寶等電商的物流配送,老是從最近的倉庫發貨,節省物流成本; 微信
無人機、無人駕駛汽車等; 網絡
…… app
這些都統稱爲基於位置的服務(LBS)。 eclipse
對於普通用戶來講,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 |
20ft(6m) |
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關於位置服務相關的源代碼。
(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)。使用完以後須要在適當的位置移除監聽:LocationManager.removeUpdates(LocationListener 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.Listener:GPS狀態監聽的一個接口
使用方法與locationListener接口相似,先實現接口並建立對象,實現接口中的方法:onGpsStatusChanged(int event);在方法中實現對衛星狀態信息變化的監聽,根據event的類型編寫邏輯代碼。建立對象後再註冊監聽:LocationManager.addGpsStatusListener(GpsStatus.Listener listener);使用後在合適的位置釋放監聽:LocationManager.removeGpsStatusListener(GpsStatus.Listener listener)
(5)、GpsStatus.NmeaListener:監聽GPS的NMEA 0183數據
這也是一個接口,用於監聽串口上的GPS數據,標準的NMEA 0183格式導航電文。不過在實際android開發中基本不使用該接口對象,採用常規的方法也獲取不到串口上的數據,具體緣由不詳。實現該接口須要實現其個方法:onNmeaReceived(long timestamp, String nmea); 而後添加監聽:LocationManager.addNmeaListener(NmeaListener 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 = new MyLocationListener(); 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);
在開發的過程當中,能夠直接在activity中實現獲取位置信息的功能,開發LBS程序。一個新的需求:須要多個activity用到實時獲取的位置信息。能夠在多個activity中編寫重複的位置服務相關的代碼邏輯也就是第四部分中的內容,重複的代碼很是多,高效的開發者絕對不會作這麼SB的事情。那麼能夠編寫一個工具類class LocationUtils,將公共的功能邏輯封裝好,在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();//使用完後移除監聽
}
}
http://javapapers.com/android/android-location-fused-provider/