做項目需要獲取經緯度信息,學習了下android自帶的定位API,簡單實現了一下,這裏記錄一下。廢話不多說,先上代碼:
private String locationStr = ""; private String message = ""; private static final int REQUEST_CODE = 10; private void getLocation() { if (Build.VERSION.SDK_INT >= 23) {// android6 執行運行時權限 if (ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // TODO: Consider calling // Activity#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for Activity#requestPermissions for more details. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE); } } Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_COARSE);//低精度,如果設置爲高精度,依然獲取不了location。 criteria.setAltitudeRequired(false);//不要求海拔 criteria.setBearingRequired(false);//不要求方位 criteria.setCostAllowed(true);//允許有花費 criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗 //獲取LocationManager LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); // 獲取最好的定位方式 String provider = locationManager.getBestProvider(criteria, true); // true 代表從打開的設備中查找 // 獲取所有可用的位置提供器 List<String> providerList = locationManager.getProviders(true); // 測試一般都在室內,這裏顛倒了書上的判斷順序 if (providerList.contains(LocationManager.NETWORK_PROVIDER)) { provider = LocationManager.NETWORK_PROVIDER; } else if (providerList.contains(LocationManager.GPS_PROVIDER)) { provider = LocationManager.GPS_PROVIDER; } else { // 當沒有可用的位置提供器時,彈出Toast提示用戶 Toast.makeText(this, "Please Open Your GPS or Location Service", Toast.LENGTH_SHORT).show(); return; } LocationListener locationListener = new LocationListener(){ //當位置改變的時候調用 @Override public void onLocationChanged(Location location) { //經度 double longitude = location.getLongitude(); //緯度 double latitude = location.getLatitude(); //海拔 double altitude = location.getAltitude(); locationStr = longitude+"_"+latitude; launcher.callExternalInterface("getLocationSuccess", locationStr); } //當GPS狀態發生改變的時候調用 @Override public void onStatusChanged(String provider, int status, Bundle extras) { switch (status) { case LocationProvider.AVAILABLE: message = "當前GPS爲可用狀態!"; break; case LocationProvider.OUT_OF_SERVICE: message = "當前GPS不在服務內!"; break; case LocationProvider.TEMPORARILY_UNAVAILABLE: message = "當前GPS爲暫停服務狀態!"; break; } launcher.callExternalInterface("GPSStatusChanged", message); } //GPS開啓的時候調用 @Override public void onProviderEnabled(String provider) { message = "GPS開啓了!"; launcher.callExternalInterface("GPSOpenSuccess", message); } //GPS關閉的時候調用 @Override public void onProviderDisabled(String provider) { message = "GPS關閉了!"; launcher.callExternalInterface("GPSClosed", message); } }; //獲取上次的location Location location = locationManager.getLastKnownLocation(provider);
/** * 參1:選擇定位的方式 * 參2:定位的間隔時間 * 參3:當位置改變多少時進行重新定位 * 參4:位置的回調監聽 */locationManager.requestLocationUpdates(provider, 10000, 0, locationListener); while(location == null){ location = locationManager.getLastKnownLocation(provider); } //移除更新監聽 locationManager.removeUpdates(locationListener); if (location != null) { //不爲空,顯示地理位置經緯度 //經度 double longitude = location.getLongitude(); //緯度 double latitude = location.getLatitude(); //海拔 double altitude = location.getAltitude(); locationStr = longitude+"_"+latitude; launcher.callExternalInterface("getLocationSuccess", locationStr); } }
/** * 獲取權限結果 */ @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission Granted准許 getLocation(); } else { // Permission Denied拒絕 } } }
簡單說明一下,getLocation()方法實現定位的一系列操作,但是安卓要調服務是需要驗證權限的,所以要複寫onRequestPermissionsResult方法。
關鍵點:
//獲取上次的location
Location location = locationManager.getLastKnownLocation(provider);
獲取最近一次的有效location,如果沒有,則返回null。也就是說最近一次必須獲取過定位才能得到lastLocation。第一次登錄或者新安裝的app是會返回null的。
那麼問題來了,如何獲取第一次的定位信息呢?可以通過下面這個方法註冊請求新的位置信息:
locationManager.requestLocationUpdates(provider, 10000, 0, locationListener);其中,provider 是使用的定位服務商,主要有
LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER, LocationManager.PASSIVE_PROVIDER
第一個是網絡定位,第二個是GPS定位,第三個是直接取緩存。LocationManager本身提供了選擇最好的provider的方法:
// 獲取最好的定位方式 String provider = locationManager.getBestProvider(criteria, true); // true 代表從打開的設備中查找
但是我在上面選擇provider時做了一個檢查的操作:
// 獲取所有可用的位置提供器 List<String> providerList = locationManager.getProviders(true); // 測試一般都在室內,這裏顛倒了書上的判斷順序 if (providerList.contains(LocationManager.NETWORK_PROVIDER)) { provider = LocationManager.NETWORK_PROVIDER; } else if (providerList.contains(LocationManager.PASSIVE_PROVIDER)) { provider = LocationManager.GPS_PROVIDER; } else { // 當沒有可用的位置提供器時,彈出Toast提示用戶 Toast.makeText(this, "Please Open Your GPS or Location Service", Toast.LENGTH_SHORT).show(); return; }
原因是API本身是GPS優先的,這樣在室內測試時會出現bestprovider得到的是GPS方式,但是卻無法定位的情況(室內GPS信號很弱,基本不可用)。所以我改成了優先選擇網絡定位,然後再選擇GPS定位。實際使用時可以去掉該段操作。
另外,locationListener是註冊的監聽事件。其中我們要關注的是
public void onLocationChanged(Location location)
這個方法會監聽上面的requestLocationUpdates,獲取到新的位置信息就會回調該方法,所以大家可以再這個方法裏處理獲取到的location。
不過,這個定位有一個很大的問題,那就是對於部分安卓設備,第一次獲取location時,會在locationManager.requestLocationUpdates處堵塞,導致程序一直卡在這裏,遲遲得不到onLocationChanged的回調。我測試了安卓5,6, 7的設備,其中兩個android5.1.1的設備一直都獲取不到location,這就導致該定位無法在此設備上使用。查了各種網站,發現有兩個網友遇到了同樣的問題,但是取沒有解決:https://segmentfault.com/q/1010000004477439/a-1020000006144410。
但是有一個奇怪的現象,就是我在android5.1.1的設備上測試的時候,偶爾是可以得到一次location的,但這個機率極低。網上有說需要等待一段時間,但是我等了個把小時都不行。
另外我也試了google的FusedLocationProviderClient方法,也是堵塞在requestLocationUpdates,實在是鬱悶。由於我做的是一個海外的項目,所以什麼百度API,騰訊API就不用想了。
這裏貼出來也是希望大神們看到之後能夠指正,並希望能幫忙解決上面這個問題,大家共同進步。