Android使用LocationManager實現(xiàn)定位功能

目錄

目錄

前言

最近有一個需要用到定位的國外項目,所以總結(jié)了一下用Android原生的LocationManager來進(jìn)行定位的方法,在此記錄一下,方便以后使用。

關(guān)鍵代碼

關(guān)鍵代碼被我封裝成了一個工具類如下:

public class LocationUtils {
    private volatile static LocationUtils uniqueInstance;
    private LocationManager locationManager;
    private Context mContext;
    private static ArrayList<AddressCallback> addressCallbacks;
    private AddressCallback addressCallback;
    public AddressCallback getAddressCallback() {
        return addressCallback;
    }

    public void setAddressCallback(AddressCallback addressCallback) {
        this.addressCallback = addressCallback;
        if(isInit){
            showLocation();
        }else {
            isInit = true;
        }
    }

    private static Location location;
    private boolean isInit = false;//是否加載過
    private LocationUtils(Context context) {
        mContext = context;
        getLocation();
    }

    //采用Double CheckLock(DCL)實現(xiàn)單例
    public static LocationUtils getInstance(Context context) {
        if (uniqueInstance == null) {
            synchronized (LocationUtils.class) {
                if (uniqueInstance == null) {
                    addressCallbacks = new ArrayList<>();
                    uniqueInstance = new LocationUtils(context);
                }
            }
        }
        return uniqueInstance;
    }

    /**
     * 添加回調(diào)事件
     * @param addressCallback
     */
    private void addAddressCallback(AddressCallback addressCallback){
        addressCallbacks.add(addressCallback);
        if(isInit){
            showLocation();
        }
    }

    /**
     * 移除回調(diào)事件
     * @param addressCallback
     */
    public void removeAddressCallback(AddressCallback addressCallback){
        if(addressCallbacks.contains(addressCallback)){
            addressCallbacks.remove(addressCallback);
        }
    }

    /**
     * 清空回調(diào)事件
     */
    public void cleareAddressCallback(){
        removeLocationUpdatesListener();
        addressCallbacks.clear();
    }
    private void getLocation() {
        //1.獲取位置管理器
        locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        //添加用戶權(quán)限申請判斷
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        //2.獲取位置提供器,GPS或是NetWork
        // 獲取所有可用的位置提供器
        List<String> providerList = locationManager.getProviders(true);
        String locationProvider;
        if (providerList.contains(LocationManager.GPS_PROVIDER)) {
            //GPS 定位的精準(zhǔn)度比較高,但是非常耗電。
            System.out.println("=====GPS_PROVIDER=====");
            locationProvider = LocationManager.GPS_PROVIDER;
        } else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {//Google服務(wù)被墻不可用
            //網(wǎng)絡(luò)定位的精準(zhǔn)度稍差,但耗電量比較少。
            System.out.println("=====NETWORK_PROVIDER=====");
            locationProvider = LocationManager.NETWORK_PROVIDER;
        } else {
            System.out.println("=====NO_PROVIDER=====");
            // 當(dāng)沒有可用的位置提供器時,彈出Toast提示用戶
            Intent intent = new Intent();
            intent.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            mContext.startActivity(intent);
            return;
        }

        //3.獲取上次的位置,一般第一次運(yùn)行,此值為null
        location = locationManager.getLastKnownLocation(locationProvider);
        if (location != null) {
            // 顯示當(dāng)前設(shè)備的位置信息
            System.out.println("==顯示當(dāng)前設(shè)備的位置信息==");
            showLocation();
        } else {//當(dāng)GPS信號弱沒獲取到位置的時候可從網(wǎng)絡(luò)獲取
            System.out.println("==Google服務(wù)被墻的解決辦法==");
            getLngAndLatWithNetwork();//Google服務(wù)被墻的解決辦法
        }
        // 監(jiān)視地理位置變化,第二個和第三個參數(shù)分別為更新的最短時間minTime和最短距離minDistace
        //LocationManager 每隔 5 秒鐘會檢測一下位置的變化情況,當(dāng)移動距離超過 10 米的時候,
        // 就會調(diào)用 LocationListener 的 onLocationChanged() 方法,并把新的位置信息作為參數(shù)傳入。
        locationManager.requestLocationUpdates(locationProvider, 5000, 10, locationListener);
    }

    //獲取經(jīng)緯度
    private void showLocation() {
        if(location == null){
            getLocation();
        }else {
            double latitude = location.getLatitude();//緯度
            double longitude = location.getLongitude();//經(jīng)度
//            for(AddressCallback addressCallback:addressCallbacks){
//                addressCallback.onGetLocation(latitude,longitude);
//            }
            if(addressCallback != null){
                addressCallback.onGetLocation(latitude,longitude);
            }
            getAddress(latitude, longitude);
        }
    }

    private void getAddress(double latitude, double longitude) {
        //Geocoder通過經(jīng)緯度獲取具體信息
        Geocoder gc = new Geocoder(mContext, Locale.getDefault());
        try {
            List<Address> locationList = gc.getFromLocation(latitude, longitude, 1);

            if (locationList != null) {
                Address address = locationList.get(0);
                String countryName = address.getCountryName();//國家
                String countryCode = address.getCountryCode();
                String adminArea = address.getAdminArea();//省
                String locality = address.getLocality();//市
                String subLocality = address.getSubLocality();//區(qū)
                String featureName = address.getFeatureName();//街道

                for (int i = 0; address.getAddressLine(i) != null; i++) {
                    String addressLine = address.getAddressLine(i);
                    //街道名稱:廣東省深圳市羅湖區(qū)蔡屋圍一街深圳瑞吉酒店
                    System.out.println("addressLine=====" + addressLine);
                }
                if(addressCallback != null){
                    addressCallback.onGetAddress(address);
                }
//                for(AddressCallback addressCallback:addressCallbacks){
//                    addressCallback.onGetAddress(address);
//                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void removeLocationUpdatesListener() {
        if (locationManager != null) {
            uniqueInstance = null;
            locationManager.removeUpdates(locationListener);
        }
    }


    private LocationListener locationListener = new LocationListener() {
        // Provider的狀態(tài)在可用、暫時不可用和無服務(wù)三個狀態(tài)直接切換時觸發(fā)此函數(shù)
        @Override
        public void onStatusChanged(String provider, int status, Bundle arg2) {
        }

        // Provider被enable時觸發(fā)此函數(shù),比如GPS被打開
        @Override
        public void onProviderEnabled(String provider) {
        }

        // Provider被disable時觸發(fā)此函數(shù),比如GPS被關(guān)閉
        @Override
        public void onProviderDisabled(String provider) {
        }

        //當(dāng)坐標(biāo)改變時觸發(fā)此函數(shù),如果Provider傳進(jìn)相同的坐標(biāo),它就不會被觸發(fā)
        @Override
        public void onLocationChanged(Location loc) {
            System.out.println("==onLocationChanged==");
//            location = loc;
//            showLocation();
        }
    };

    //從網(wǎng)絡(luò)獲取經(jīng)緯度
    private void getLngAndLatWithNetwork() {
        //添加用戶權(quán)限申請判斷
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 10, locationListener);
        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        showLocation();
    }
    public interface AddressCallback{
        void onGetAddress(Address address);
        void onGetLocation(double lat,double lng);
    }
}

工具類的使用方法:

//注意6.0及以上版本需要在申請完權(quán)限后調(diào)用方法
LocationUtils.getInstance(this).setAddressCallback(new LocationUtils.AddressCallback() {
            @Override
            public void onGetAddress(Address address) {
                String countryName = address.getCountryName();//國家
                String adminArea = address.getAdminArea();//省
                String locality = address.getLocality();//市
                String subLocality = address.getSubLocality();//區(qū)
                String featureName = address.getFeatureName();//街道
                LogUtils.eTag("定位地址",countryName,adminArea,locality,subLocality,featureName);
            }

            @Override
            public void onGetLocation(double lat, double lng) {
                LogUtils.eTag("定位經(jīng)緯度",lat,lng);
            }
        });

需要的權(quán)限(6.0以上別忘了動態(tài)申請權(quán)限):

<!--允許程序打開網(wǎng)絡(luò)套接字-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允許程序設(shè)置內(nèi)置sd卡的寫權(quán)限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允許程序獲取網(wǎng)絡(luò)狀態(tài)-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允許程序訪問WiFi網(wǎng)絡(luò)信息-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--允許程序讀寫手機(jī)狀態(tài)和身份-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--允許程序訪問CellID或WiFi熱點來獲取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

注意

某些版本的手機(jī)在使用Geocoder進(jìn)行地址轉(zhuǎn)換的時候會出現(xiàn)如下異常:
java.io.IOException: grpc failed
原因為:the service is not available 服務(wù)不可用 即設(shè)備不支持Geocoder
這時可以使用Google的API接口進(jìn)行解析,API接口如下:
https://maps.googleapis.com/maps/api/geocode/json?latlng=37.422,-122.084&language=zh-CN&sensor=false&key=YOUR_API_KEY
這里的key需要去GoogleMap開發(fā)平臺去申請

案例源碼

https://github.com/myml666/LocationDemo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容