網站首頁 編程語言 正文
前言
現在的應用,幾乎每一個 App 都存在定位的邏輯,方便更好的推薦產品或服務,獲取當前設備的經緯度是必備的功能了。有些 App 還是以LBS(基于位置服務)為基礎來實現的,比如美團,餓了嗎,不獲取到位置都無法使用的。
有些同學覺得不就是獲取到經緯度么,Android 自帶的就有位置服務 LocationManager ,我們無需引入第三方服務,就可以很方便的實現定位邏輯。
確實 LocationManager 的使用很簡單,獲取經緯度很方便,我們就無需第三方的服務了嗎? 或者說 LocationManager 有沒有坑呢?兼容性問題怎么樣?獲取不到位置有沒有什么兜底策略?
一、LocationManager的使用
由于是Android的系統服務,直接 getSystemService 可以獲取到
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
一般獲取位置有兩種方式 NetWork 與 GPS 。我們可以指定方式,也可以讓系統自動提供最好的方式。
// 獲取所有可用的位置提供器
List<String> providerList = locationManager.getProviders(true);
// 可以指定優先GPS,再次網絡定位
if (providerList.contains(LocationManager.GPS_PROVIDER)) {
provider = LocationManager.GPS_PROVIDER;
} else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
provider = LocationManager.NETWORK_PROVIDER;
} else {
// 當沒有可用的位置提供器時,彈出Toast提示用戶
return;
}
當然我更推薦由系統提供,當我的設備在室內的時候就會以網絡的定位提供,當設備在室外的時候就可以提供GPS定位。
String provider = locationManager.getBestProvider(criteria, true);
我們可以實現一個定位的Service實現這個邏輯
/**
* 獲取定位服務
*/
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);//允許有花費
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) {
//不為空,顯示地理位置經緯度
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 {
// 位置改變時獲取經緯度
@Override
public void onLocationChanged(Location location) {
String longitude = "Longitude:" + location.getLongitude();
String latitude = "Latitude:" + location.getLatitude();
YYLogUtils.w("onLocationChanged:" + longitude + "-" + latitude);
stopSelf(); // 獲取到經緯度以后,停止該service
}
// 狀態改變時
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
YYLogUtils.w("onStatusChanged - provider:"+provider +" status:"+status);
}
// 提供者可以使用時
@Override
public void onProviderEnabled(String provider) {
YYLogUtils.w("GPS開啟了");
}
// 提供者不可以使用時
@Override
public void onProviderDisabled(String provider) {
YYLogUtils.w("GPS關閉了");
}
}
@Override
public void onDestroy() {
super.onDestroy();
lm.removeUpdates(listener); // 停止所有的定位服務
}
}
使用:定義并動態申請權限之后即可開啟服務
fun testLocation() {
extRequestPermission(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) {
startService(Intent(mActivity, LocationService::class.java))
}
}
這樣我們啟動這個服務就可以獲取到當前的經緯度,只是獲取一次,大家如果想再后臺持續定位,那么實現的方式就不同了,我們服務要設置為前臺服務,并且需要額外申請后臺定位權限。
話說回來,這么使用就一定能獲取到經緯度嗎?有沒有兼容性問題
Android 5.0 Oppo
Android 6.0 Oppo海外版
Android 7.0 華為
Android 11 三星海外版
Android 12 vivo
目前測試不多,也能發現問題,特別是一些低版本,老系統的手機就可能無法獲取位置,應該是系統的問題,這種服務跟網絡沒關系,開不開代理都是一樣的。
并且隨著測試系統的變高,越來越完善,提供的最好定位方式還出現混合定位 fused 的選項。
那是不是6.0的Oppo手機太老了,不支持定位了?并不是,百度定位可以獲取到位置的。
既然只使用 LocationManager 有風險,有可能無法獲取到位置,那怎么辦?
二、混合定位
其實目前百度,高度的定位Api的服務SDK也不算大,相比地圖導航等比較重的功能,定位的SDK很小了,并且目前都支持海外的定位服務。并且定位服務是免費的哦。
既然 LocationManager 有可能獲取不到位置,那我們就加入第三方定位服務,比如百度定位。我們同時使用 LocationManager 和百度定位,哪個先成功就用哪一個。(如果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();
}
/**
* 第三方百度定位服務
*/
private void createBDLocation() {
mBDLocationClient = new LocationClient(UIUtils.getContext());
mBDLocationListener = new MyBDLocationListener();
//聲明LocationClient類
mBDLocationClient.registerLocationListener(mBDLocationListener);
//配置百度定位的選項
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();
}
/**
* 原生的定位服務
*/
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);//允許有花費
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) {
//不為空,顯示地理位置經緯度
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 {
// 位置改變時獲取經緯度
@Override
public void onLocationChanged(Location location) {
String longitude = "Longitude:" + location.getLongitude();
String latitude = "Latitude:" + location.getLatitude();
YYLogUtils.w("onLocationChanged:" + longitude + "-" + latitude);
stopSelf(); // 獲取到經緯度以后,停止該service
}
// 狀態改變時
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
YYLogUtils.w("onStatusChanged - provider:" + provider + " status:" + status);
}
// 提供者可以使用時
@Override
public void onProviderEnabled(String provider) {
YYLogUtils.w("GPS開啟了");
}
// 提供者不可以使用時
@Override
public void onProviderDisabled(String provider) {
YYLogUtils.w("GPS關閉了");
}
}
/**
* 百度定位的監聽
*/
class MyBDLocationListener extends BDAbstractLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
double latitude = location.getLatitude(); //獲取緯度信息
double longitude = location.getLongitude(); //獲取經度信息
YYLogUtils.w("百度的監聽 latitude:" + latitude);
YYLogUtils.w("百度的監聽 longitude:" + longitude);
YYLogUtils.w("onBaiduLocationChanged:" + longitude + "-" + latitude);
stopSelf(); // 獲取到經緯度以后,停止該service
}
}
@Override
public void onDestroy() {
super.onDestroy();
// 停止所有的定位服務
lm.removeUpdates(listener);
mBDLocationClient.stop();
mBDLocationClient.unregisterLocationListener(mBDLocationListener);
}
}
其實邏輯都是很簡單的,并且省略了不少回調通信的邏輯,這里只涉及到定位的邏輯,別的邏輯我就盡量不涉及到。
百度定位服務的API申請與初始化請自行完善,這里只是簡單的使用。并且坐標系統一為國際坐標,如果需要轉gcj02的坐標系,可以網上找個工具類,或者看我之前的文章。
獲取到位置之后,如何Service與Activity通信,就由大家自由發揮了,有興趣的可以看我之前的文章。
總結
所以說Android原生定位服務 LocationManager 還是有問題啊,低版本的設備可能不行,高版本的Android系統又很行,兼容性有問題!讓人又愛又恨。
很羨慕iOS的定位服務,真的好用,我們 Android 的定位服務真是拉跨,居然還有兼容性問題。
我們使用第三方定位服務和自己的 LocationManager 并發獲取位置,這樣可以增加容錯率。是比較好用的,為什么要加上 LocationManager 呢?
我直接單獨用第三方的定位服務不香嗎?可以是可以,但是如果設備支持 LocationManager 的話,它會更快一點,體驗更好。
原文鏈接:https://juejin.cn/post/7131910145574961182
相關推薦
- 2022-06-12 Android開發之保存圖片到相冊的三種方法詳解_Android
- 2023-01-08 Android智能指針輕量級Light?Pointer初識_Android
- 2023-04-19 SQLSERVER?的?truncate?和?delete?區別解析_MsSql
- 2022-11-10 rust延遲5秒鎖屏的實現代碼_相關技巧
- 2022-09-19 正則表達式regexp_replace的使用方法_正則表達式
- 2022-04-16 spyder快捷鍵與python符號化輸出方式_python
- 2022-03-29 python中apply函數詳情_python
- 2023-06-18 Python實現兩種稀疏矩陣的最小二乘法_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支