android中几种定位方式详解
目錄
前言:
1、GPS定位
2、NETWORK定位
3、AGPS定位
4、基站定位
5、WIFI定位
6、混合定位
目前,移動端大致通過三種方式來進行設(shè)備定位:GPS、基站、wifi。本文就詳細的講解一下這幾種定位方式和實現(xiàn)方法。
前言:
android中我們一般使用LocationManager來獲取位置信息,這里面有四中provider:
public static final String NETWORK_PROVIDER = "network"; public static final String GPS_PROVIDER = "gps"; public static final String PASSIVE_PROVIDER = "passive"; public static final String FUSED_PROVIDER = "fused";區(qū)別如下:
(1)GPS_PROVIDER:通過 GPS 來獲取地理位置的經(jīng)緯度信息; 優(yōu)點:獲取地理位置信息精確度高; 缺點:只能在戶外使用,獲取經(jīng)緯度信息耗時,耗電;
(2)NETWORK_PROVIDER:通過移動網(wǎng)絡(luò)的基站或者 Wi-Fi 來獲取地理位置; 優(yōu)點:只要有網(wǎng)絡(luò),就可以快速定位,室內(nèi)室外都可; 缺點:精確度不高;
(3)PASSIVE_PROVIDER:被動接收更新地理位置信息,而不用自己請求地理位置信息。
PASSIVE_PROVIDER 返回的位置是通過其他 providers 產(chǎn)生的,可以查詢 getProvider() 方法決定位置更新的由來,需要 ACCESS_FINE_LOCATION 權(quán)限,但是如果未啟用 GPS,則此 provider 可能只返回粗略位置匹配;
(4)FUSED_PROVIDER:這個本來已經(jīng)被廢棄了,但是目前在Android12(即android api 31)上又重新使用了起來,但是它依賴GMS,所以國內(nèi)暫時無法使用。
我們通常使用gps和network這兩種方式。
但是我們還可以通過其它方式獲取位置信息,這篇文章就詳細的講解一下在android中幾種獲取定位的方式。
1、GPS定位
這個用的最普遍,可以獲取上次定位,也可以監(jiān)聽變化,代碼如下:?
需要權(quán)限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>GPS定位
var locManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager var loc = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) if(loc != null){Log.e("gpslocation", loc.toString())toast(loc.toString()) } locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0F, object : LocationListener{override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}override fun onProviderEnabled(provider: String?) {}override fun onProviderDisabled(provider: String?) {}override fun onLocationChanged(location: Location?) {Log.e("gpslocation", location.toString())toast(location.toString())} })2、NETWORK定位
與gps定位代碼基本一致,只不過將provider改成LocationManager.NETWORK_PROVIDER
3、AGPS定位
實際上是將上面兩種定位結(jié)合起來,具體原理如下:
AGPS手機首先將本身的基站地址通過網(wǎng)絡(luò)傳輸?shù)轿恢梅?wù)器;
位置服務(wù)器根據(jù)該手機的大概位置傳輸與該位置相關(guān)的GPS輔助信息(包含GPS的星歷和方位俯仰角等)到手機;
該手機的AGPS模塊根據(jù)輔助信息(以提升GPS信號的第一鎖定時間TTFF能力)接收GPS原始信號;
手機在接收到GPS原始信號后解調(diào)信號,計算手機到衛(wèi)星的偽距(偽距為受各種GPS誤差影響的距離),并將有關(guān)信息通過網(wǎng)絡(luò)傳輸?shù)轿恢梅?wù)器;
位置服務(wù)器根據(jù)傳來的GPS偽距信息和來自其他定位設(shè)備(如差分GPS基準(zhǔn)站等)的輔助信息完成對GPS信息的處理,并估算該手機的位置;
位置服務(wù)器將該手機的位置通過網(wǎng)絡(luò)傳輸?shù)蕉ㄎ痪W(wǎng)關(guān)或應(yīng)用平臺。
我的理解就是通過網(wǎng)絡(luò)位置和位置服務(wù)器判斷出最佳的衛(wèi)星,減少了獲取衛(wèi)星信號的時間。因為網(wǎng)絡(luò)位置獲取很快,所以可以減少整體的定位時間。
AGPS并不是一種定位方式,只是一種優(yōu)化方案,代碼與GPS一樣,只不過在設(shè)置中將定位模式設(shè)成AGPS。
上面是android自帶的定位方式,我們還可以獲取一些原始信息(比如基站信息、wifi信息),通過公開的接口來獲取位置信息。下面幾種方式就是使用原始信息通過API來獲取位置信息。
4、基站定位
通過TelephonyManager我們可以拿到基站信息,再通過相關(guān)的api接口就能得到經(jīng)緯度,但是基站定位精度很差。
基站信息包含如下:
MCC,Mobile Country Code,移動國家代碼(中國的為460);
MNC,Mobile Network Code,移動網(wǎng)絡(luò)號碼(中國移動為00,中國聯(lián)通為01);
LAC,Location Area Code,位置區(qū)域碼;
CID,Cell Identity,基站編號,是個16位的數(shù)據(jù)(范圍是0到65535)。
拿到這個信息后,我們可以通過一些公開的api服務(wù)拿到經(jīng)緯度,如下:
http://www.google.com/loc/json? ?google的,post請求,好像停用了
接口說明文檔 | LBS數(shù)據(jù)倉庫? 目前可用,是免費的
實例:
http://api.cellocation.com:82/cell/?mcc=460&mnc=1&lac=4301&ci=20986&output=json
返回:
{"errcode":0, "lat":"40.00598145", "lon":"116.48539734", "radius":"937", "address":"北京市朝陽區(qū)來廣營地區(qū)東湖渠;溪陽東路與屏翠東路路口東70米"}
接口參數(shù):
| 名稱 | 類型 | 必填 | 說明 |
| mcc | int | 是 | mcc國家代碼:中國代碼 460 |
| mnc | int | 是 | mnc網(wǎng)絡(luò)類型:0移動,1聯(lián)通(電信對應(yīng)sid),十進制 |
| lac | int | 是 | lac(電信對應(yīng)nid),十進制 |
| ci | int | 是 | cellid(電信對應(yīng)bid),十進制 |
| coord | string | 否 | 坐標(biāo)類型(wgs84/gcj02/bd09),默認wgs84 |
| output | string | 否 | 返回格式(csv/json/xml),默認csv |
另外還有很多提供這種接口和數(shù)據(jù)的平臺,自己搜索即可
代碼如下:
需要權(quán)限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>?基站定位,這里只實現(xiàn)了GSM的,CDMA的有些許不同
val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager if(telManager.cellLocation is GsmCellLocation) {val cellLoc = telManager.cellLocation as GsmCellLocationif (cellLoc != null) {val operator = telManager.networkOperatorval mcc = operator.substring(0, 3).toInt()val mnc = operator.substring(3).toInt()val cid = cellLoc.cidval lac = cellLoc.lac...doAsync {val result = URL(sb.toString()).readText()Log.e("tellocation", result)}} }5、WIFI定位
wifi定位是通過WifiManager拿到wifi的信息,主要是wifi的BSSID(即mac地址)。然后通過一些api查詢經(jīng)緯度,比如接口說明文檔 | LBS數(shù)據(jù)倉庫
實例:
http://api.cellocation.com:82/wifi/?mac=00:87:36:05:5d:ea&output=json
返回:
{"errcode":0, "lat":"39.950008", "lon":"116.230049", "radius":"222", "address":"北京市海淀區(qū)四季青鎮(zhèn)益園文創(chuàng)基地c區(qū)9號樓;南平莊中路與西平莊路路口西北561米"}
接口參數(shù):
| 名稱 | 類型 | 必填 | 說明 |
| mac | string | 是 | WIFI熱點的MAC地址(BSSID) |
| coord | string | 否 | 坐標(biāo)類型(wgs84/gcj02/bd09),默認wgs84 |
| output | string | 否 | 返回格式(csv/json/xml),默認csv |
代碼如下:
需要權(quán)限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>?wifi定位
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager if(wifiManager.isWifiEnabled) {var mac = wifiManager.connectionInfo.bssidif(TextUtils.isEmpty(mac)) {/*** 當(dāng)未鏈接wifi時,可以使用掃描到的wifi列表中找一個信號強度最好的。這里沒進行比較,直接使用第一個了* ScanReuslt有三個字段比較重要:SSID是wifi名稱,BSSID是wifi的mac,level則是信號強度(負數(shù))* 注意結(jié)果中同一個SSID可能會有多個,如果需要鏈接wifi可以通過信號強度過濾出最好的來鏈接*/val scanlist = wifiManager.scanResultsfor(info in scanlist){Log.e("wifiinfo", info.toString())}if(scanlist.size > 0) {mac = wifiManager.scanResults[0].BSSID}}if(!TextUtils.isEmpty(mac)) {...doAsync {val result = URL(sb.toString()).readText()Log.e("wifilocation", result)}} }6、混合定位
混合定位就是獲取附近的wifi列表信息(包括信號強度)和附近的基站列表信息(包括信號強度),通過一些api獲取經(jīng)緯度。
這種方式相對于單一的基站和wifi定位要更精確一些。
獲取附近的wifi列表在WIFI定位已經(jīng)提到過了,通過WifiManager的getScanResults函數(shù)獲取掃描到的wifi列表,其中l(wèi)evel就是信號強度,可能需要做一下去重。
獲取附近的基站列表則有些問題。
官方提供了一個方式,通過TelephonyManager的getNeighboringCellInfo函數(shù)獲得,其中mRssi就是信號強度。
需要權(quán)限
<uses-permission?android:name="android.permission.ACCESS_COARSE_LOCATION"/>獲取附近基站信息
val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val neighborCells = telManager.neighboringCellInfo for(cell in neighborCells){//這里將rssi轉(zhuǎn)化為dBmval level = -131 + 2 * cell.rssiLog.e("neighboringCellInfo", "cid:${cell.cid} lac:${cell.lac} rssi:$level") }但是實際使用時發(fā)現(xiàn)cid和lac都是-1。
檢查NeighboringCellInfo的構(gòu)造方法,如下:
public NeighboringCellInfo(int rssi, String location, int radioType) {// set default valuemRssi = rssi;mNetworkType = NETWORK_TYPE_UNKNOWN;mPsc = UNKNOWN_CID;mLac = UNKNOWN_CID;mCid = UNKNOWN_CID;// pad location string with leading "0"int l = location.length();if (l > 8) return;if (l < 8) {for (int i = 0; i < (8-l); i++) {location = "0" + location;}}// TODO - handle LTE and eHRPD (or find they can't be supported)try {// set LAC/CID or PSC based on radioTypeswitch (radioType) {case NETWORK_TYPE_GPRS:case NETWORK_TYPE_EDGE:mNetworkType = radioType;// check if 0xFFFFFFFF for UNKNOWN_CIDif (!location.equalsIgnoreCase("FFFFFFFF")) {mCid = Integer.parseInt(location.substring(4), 16);mLac = Integer.parseInt(location.substring(0, 4), 16);}break;case NETWORK_TYPE_UMTS:case NETWORK_TYPE_HSDPA:case NETWORK_TYPE_HSUPA:case NETWORK_TYPE_HSPA:mNetworkType = radioType;mPsc = Integer.parseInt(location, 16);break;}} catch (NumberFormatException e) {// parsing location errormPsc = UNKNOWN_CID;mLac = UNKNOWN_CID;mCid = UNKNOWN_CID;mNetworkType = NETWORK_TYPE_UNKNOWN;}}可以看到只對GPRS和EDGE網(wǎng)絡(luò)進行了處理,而3G、4G網(wǎng)絡(luò)都是UNKNOWN_CID,即-1。說明這種方法不支持,已經(jīng)過時。
官方還有另外一個方式,通過TelephonyManager的getAllCellInfo函數(shù)獲得。這個函數(shù)要求minsdkverison必須在17及以上
需要權(quán)限
<uses-permission?android:name="android.permission.ACCESS_COARSE_LOCATION"/>獲取基站信息,這里只處理了LTE網(wǎng)絡(luò)的
val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val cells = telManager.allCellInfo for(cell in cells){if(cell is CellInfoLte) {Log.e("celllist", "cid:${cell.cellIdentity.ci} lac:${cell.cellIdentity.tac} dbm:${cell.cellSignalStrength.dbm} isRegistered:${cell.isRegistered}")} }得到的信息如下:
E/celllist: cid:3912981 lac:4154 dbm:-87 isRegistered:true
E/celllist: cid:2147483647 lac:2147483647 dbm:-101 isRegistered:false
E/celllist: cid:2147483647 lac:2147483647 dbm:-102 isRegistered:false
E/celllist: cid:2147483647 lac:2147483647 dbm:-108 isRegistered:false
E/celllist: cid:2147483647 lac:2147483647 dbm:-101 isRegistered:false
其中第一個是我們正在使用的基站,可以看到正常的返回了信息,而其余的則返回默認的信息(Integer.MAX_VALUE)
說明這個方法也只能拿到當(dāng)前使用的基站信息。而且據(jù)網(wǎng)上的說法,當(dāng)使用2G網(wǎng)絡(luò),getAllCellInfo得到的是NULL。
這樣目前沒有更好的方式獲取多個基站信息了。
當(dāng)我們拿到附近的基站信息和wifi信息,可以通過http://www.cellocation.com/interfac/ 提供的混合定位接口查詢位置信息,與上面類似,這里不細說了。
總結(jié)
一般情況下,我們使用系統(tǒng)提供的LocationManager即可獲取位置信息,方便簡單。如果我們有自己的基站或wifi信息庫,也可以獲取相關(guān)源信息通過接口來實現(xiàn)個性化服務(wù)。
源碼
關(guān)注公眾號:BennuCTech,發(fā)送“多定位”獲取源碼
?
總結(jié)
以上是生活随笔為你收集整理的android中几种定位方式详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 探讨8.0版本下后台service存活机
- 下一篇: Android 8.0 targetsd