Android開發使用LocationManager實現定位服務

做項目需要獲取經緯度信息,學習了下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就不用想了。

這裏貼出來也是希望大神們看到之後能夠指正,並希望能幫忙解決上面這個問題,大家共同進步。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章