Android原生定位服務(wù)LocationManager
前言
現(xiàn)在的應(yīng)用,幾乎每一個(gè) App 都存在定位的邏輯,方便更好的推薦產(chǎn)品或服務(wù),獲取當(dāng)前設(shè)備的經(jīng)緯度是必備的功能了。有些 App 還是以LBS(基于位置服務(wù))為基礎(chǔ)來實(shí)現(xiàn)的,比如美團(tuán),餓了嗎,不獲取到位置都無法使用的。
有些同學(xué)覺得不就是獲取到經(jīng)緯度么,Android 自帶的就有位置服務(wù) LocationManager ,我們無需引入第三方服務(wù),就可以很方便的實(shí)現(xiàn)定位邏輯。
確實(shí) LocationManager 的使用很簡(jiǎn)單,獲取經(jīng)緯度很方便,我們就無需第三方的服務(wù)了嗎? 或者說 LocationManager 有沒有坑呢?兼容性問題怎么樣?獲取不到位置有沒有什么兜底策略?
一、LocationManager的使用
由于是Android的系統(tǒng)服務(wù),直接 getSystemService 可以獲取到
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
一般獲取位置有兩種方式 NetWork 與 GPS 。我們可以指定方式,也可以讓系統(tǒng)自動(dòng)提供最好的方式。
// 獲取所有可用的位置提供器 List<String> providerList = locationManager.getProviders(true); // 可以指定優(yōu)先GPS,再次網(wǎng)絡(luò)定位 if (providerList.contains(LocationManager.GPS_PROVIDER)) { provider = LocationManager.GPS_PROVIDER; } else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) { provider = LocationManager.NETWORK_PROVIDER; } else { // 當(dāng)沒有可用的位置提供器時(shí),彈出Toast提示用戶 return; }
當(dāng)然我更推薦由系統(tǒng)提供,當(dāng)我的設(shè)備在室內(nèi)的時(shí)候就會(huì)以網(wǎng)絡(luò)的定位提供,當(dāng)設(shè)備在室外的時(shí)候就可以提供GPS定位。
String provider = locationManager.getBestProvider(criteria, true);
我們可以實(shí)現(xiàn)一個(gè)定位的Service實(shí)現(xiàn)這個(gè)邏輯
/** * 獲取定位服務(wù) */ public class LocationService extends Service { private LocationManager lm; private MyLocationListener listener; @Override public IBinder onBind(Intent intent) { return null; } @SuppressLint("MissingPermission") @Override public void onCreate() { super.onCreate(); lm = (LocationManager) getSystemService(LOCATION_SERVICE); listener = new MyLocationListener(); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_COARSE); criteria.setAltitudeRequired(false);//不要求海拔 criteria.setBearingRequired(false);//不要求方位 criteria.setCostAllowed(true);//允許有花費(fèi) criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗 String provider = lm.getBestProvider(criteria, true); YYLogUtils.w("定位的provider:" + provider); Location location = lm.getLastKnownLocation(provider); YYLogUtils.w("location-" + location); if (location != null) { //不為空,顯示地理位置經(jīng)緯度 String longitude = "Longitude:" + location.getLongitude(); String latitude = "Latitude:" + location.getLatitude(); YYLogUtils.w("getLastKnownLocation:" + longitude + "-" + latitude); stopSelf(); } //第二個(gè)參數(shù)是間隔時(shí)間 第三個(gè)參數(shù)是間隔多少距離,這里我試過了不同的各種組合,能獲取到位置就是能,不能獲取就是不能 lm.requestLocationUpdates(provider, 3000, 10, listener); } class MyLocationListener implements LocationListener { // 位置改變時(shí)獲取經(jīng)緯度 @Override public void onLocationChanged(Location location) { String longitude = "Longitude:" + location.getLongitude(); String latitude = "Latitude:" + location.getLatitude(); YYLogUtils.w("onLocationChanged:" + longitude + "-" + latitude); stopSelf(); // 獲取到經(jīng)緯度以后,停止該service } // 狀態(tài)改變時(shí) @Override public void onStatusChanged(String provider, int status, Bundle extras) { YYLogUtils.w("onStatusChanged - provider:"+provider +" status:"+status); } // 提供者可以使用時(shí) @Override public void onProviderEnabled(String provider) { YYLogUtils.w("GPS開啟了"); } // 提供者不可以使用時(shí) @Override public void onProviderDisabled(String provider) { YYLogUtils.w("GPS關(guān)閉了"); } } @Override public void onDestroy() { super.onDestroy(); lm.removeUpdates(listener); // 停止所有的定位服務(wù) } }
使用:定義并動(dòng)態(tài)申請(qǐng)權(quán)限之后即可開啟服務(wù)
fun testLocation() { extRequestPermission(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) { startService(Intent(mActivity, LocationService::class.java)) } }
這樣我們啟動(dòng)這個(gè)服務(wù)就可以獲取到當(dāng)前的經(jīng)緯度,只是獲取一次,大家如果想再后臺(tái)持續(xù)定位,那么實(shí)現(xiàn)的方式就不同了,我們服務(wù)要設(shè)置為前臺(tái)服務(wù),并且需要額外申請(qǐng)后臺(tái)定位權(quán)限。
話說回來,這么使用就一定能獲取到經(jīng)緯度嗎?有沒有兼容性問題
Android 5.0 Oppo
Android 6.0 Oppo海外版
Android 7.0 華為
Android 11 三星海外版
Android 12 vivo
目前測(cè)試不多,也能發(fā)現(xiàn)問題,特別是一些低版本,老系統(tǒng)的手機(jī)就可能無法獲取位置,應(yīng)該是系統(tǒng)的問題,這種服務(wù)跟網(wǎng)絡(luò)沒關(guān)系,開不開代理都是一樣的。
并且隨著測(cè)試系統(tǒng)的變高,越來越完善,提供的最好定位方式還出現(xiàn)混合定位 fused 的選項(xiàng)。
那是不是6.0的Oppo手機(jī)太老了,不支持定位了?并不是,百度定位可以獲取到位置的。
既然只使用 LocationManager 有風(fēng)險(xiǎn),有可能無法獲取到位置,那怎么辦?
二、混合定位
其實(shí)目前百度,高度的定位Api的服務(wù)SDK也不算大,相比地圖導(dǎo)航等比較重的功能,定位的SDK很小了,并且目前都支持海外的定位服務(wù)。并且定位服務(wù)是免費(fèi)的哦。
既然 LocationManager 有可能獲取不到位置,那我們就加入第三方定位服務(wù),比如百度定位。我們同時(shí)使用 LocationManager 和百度定位,哪個(gè)先成功就用哪一個(gè)。(如果LocationManager可用的話,它的定位比百度定位更快的)
完整代碼如下:
@SuppressLint("MissingPermission") public class LocationService extends Service { private LocationManager lm; private MyLocationListener listener; private LocationClient mBDLocationClient = null; private MyBDLocationListener mBDLocationListener; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); createNativeLocation(); createBDLocation(); } /** * 第三方百度定位服務(wù) */ private void createBDLocation() { mBDLocationClient = new LocationClient(UIUtils.getContext()); mBDLocationListener = new MyBDLocationListener(); //聲明LocationClient類 mBDLocationClient.registerLocationListener(mBDLocationListener); //配置百度定位的選項(xiàng) LocationClientOption option = new LocationClientOption(); option.setLocationMode(LocationClientOption.LocationMode.Battery_Saving); option.setCoorType("WGS84"); option.setScanSpan(10000); option.setIsNeedAddress(true); option.setOpenGps(true); option.SetIgnoreCacheException(false); option.setWifiCacheTimeOut(5 * 60 * 1000); option.setEnableSimulateGps(false); mBDLocationClient.setLocOption(option); //開啟百度定位 mBDLocationClient.start(); } /** * 原生的定位服務(wù) */ private void createNativeLocation() { lm = (LocationManager) getSystemService(LOCATION_SERVICE); listener = new MyLocationListener(); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_COARSE); criteria.setAltitudeRequired(false);//不要求海拔 criteria.setBearingRequired(false);//不要求方位 criteria.setCostAllowed(true);//允許有花費(fèi) criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗 String provider = lm.getBestProvider(criteria, true); YYLogUtils.w("定位的provider:" + provider); Location location = lm.getLastKnownLocation(provider); YYLogUtils.w("location-" + location); if (location != null) { //不為空,顯示地理位置經(jīng)緯度 String longitude = "Longitude:" + location.getLongitude(); String latitude = "Latitude:" + location.getLatitude(); YYLogUtils.w("getLastKnownLocation:" + longitude + "-" + latitude); stopSelf(); } lm.requestLocationUpdates(provider, 3000, 10, listener); } class MyLocationListener implements LocationListener { // 位置改變時(shí)獲取經(jīng)緯度 @Override public void onLocationChanged(Location location) { String longitude = "Longitude:" + location.getLongitude(); String latitude = "Latitude:" + location.getLatitude(); YYLogUtils.w("onLocationChanged:" + longitude + "-" + latitude); stopSelf(); // 獲取到經(jīng)緯度以后,停止該service } // 狀態(tài)改變時(shí) @Override public void onStatusChanged(String provider, int status, Bundle extras) { YYLogUtils.w("onStatusChanged - provider:" + provider + " status:" + status); } // 提供者可以使用時(shí) @Override public void onProviderEnabled(String provider) { YYLogUtils.w("GPS開啟了"); } // 提供者不可以使用時(shí) @Override public void onProviderDisabled(String provider) { YYLogUtils.w("GPS關(guān)閉了"); } } /** * 百度定位的監(jiān)聽 */ class MyBDLocationListener extends BDAbstractLocationListener { @Override public void onReceiveLocation(BDLocation location) { double latitude = location.getLatitude(); //獲取緯度信息 double longitude = location.getLongitude(); //獲取經(jīng)度信息 YYLogUtils.w("百度的監(jiān)聽 latitude:" + latitude); YYLogUtils.w("百度的監(jiān)聽 longitude:" + longitude); YYLogUtils.w("onBaiduLocationChanged:" + longitude + "-" + latitude); stopSelf(); // 獲取到經(jīng)緯度以后,停止該service } } @Override public void onDestroy() { super.onDestroy(); // 停止所有的定位服務(wù) lm.removeUpdates(listener); mBDLocationClient.stop(); mBDLocationClient.unregisterLocationListener(mBDLocationListener); } }
其實(shí)邏輯都是很簡(jiǎn)單的,并且省略了不少回調(diào)通信的邏輯,這里只涉及到定位的邏輯,別的邏輯我就盡量不涉及到。
百度定位服務(wù)的API申請(qǐng)與初始化請(qǐng)自行完善,這里只是簡(jiǎn)單的使用。并且坐標(biāo)系統(tǒng)一為國(guó)際坐標(biāo),如果需要轉(zhuǎn)gcj02的坐標(biāo)系,可以網(wǎng)上找個(gè)工具類,或者看我之前的文章。
獲取到位置之后,如何Service與Activity通信,就由大家自由發(fā)揮了,有興趣的可以看我之前的文章。
總結(jié)
所以說Android原生定位服務(wù) LocationManager 還是有問題啊,低版本的設(shè)備可能不行,高版本的Android系統(tǒng)又很行,兼容性有問題!讓人又愛又恨。
很羨慕iOS的定位服務(wù),真的好用,我們 Android 的定位服務(wù)真是拉跨,居然還有兼容性問題。
我們使用第三方定位服務(wù)和自己的 LocationManager 并發(fā)獲取位置,這樣可以增加容錯(cuò)率。是比較好用的,為什么要加上 LocationManager 呢?
我直接單獨(dú)用第三方的定位服務(wù)不香嗎?可以是可以,但是如果設(shè)備支持 LocationManager 的話,它會(huì)更快一點(diǎn),體驗(yàn)更好。
以上就是Android原生定位服務(wù)LocationManager的詳細(xì)內(nèi)容,更多關(guān)于Android定位LocationManager的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Flutter調(diào)用Android和iOS原生代碼的方法示例
這篇文章主要給大家介紹了關(guān)于Flutter調(diào)用Android和iOS原生代碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Android廣播接實(shí)現(xiàn)監(jiān)聽電話狀態(tài)(電話的狀態(tài),攔截)
這篇文章主要介紹了Android廣播接實(shí)現(xiàn)監(jiān)聽電話狀態(tài)(電話的狀態(tài),攔截) 的相關(guān)資料,需要的朋友可以參考下2016-03-03Android自定義View實(shí)現(xiàn)九宮格圖形解鎖(Kotlin版)
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)九宮格圖形解鎖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Android中imageView圖片放大縮小及旋轉(zhuǎn)功能示例代碼
這篇文章主要介紹了Android中imageView圖片放大縮小及旋轉(zhuǎn)功能示例代碼,需要的朋友可以參考下2017-08-08Android中TimePicker與DatePicker時(shí)間日期選擇組件的使用實(shí)例
這篇文章主要介紹了Android中TimePicker時(shí)間選擇與DatePicker日期選擇組件的使用實(shí)例,這兩個(gè)組件加上去的效果就是我們平時(shí)在iOS上設(shè)置鬧鐘時(shí)調(diào)整時(shí)間類似的滾動(dòng)選項(xiàng),需要的朋友可以參考下2016-04-04