Android定位辅助工具类-------快速实现第三方定位SDK集成与使用

在开发中,会遇到一些定位的需求,我们可以使用系统提供的API来实现定位功能,但是大部分时候我们都会选择使用第三方定位SDK来实现定位功能。比如目前国内常用的定位SDK有百度地图、高德地图、腾讯地图等。

这些定位SDK都有详细的接入文档,使用也很方便,但是我自己在使用时还是会有一些问题:

1.动态权限的获取。

2.对SDK的替换,比如在使用百度的SDK时出现了一些问题,需要替换到高德SDK,不可能去一个一个的替换含有百度定位的界面。

为了让自己使用这些SDK更舒心,我自己写了一个定位的辅助工具类,既然可以更快速的对第三方SDK进行集成,也解决的上面的问题。

首先,抽取一个定位回调监听接口,包含了定位成功、定位失败、没有权限的回调方法。在定位成功时locationSuccessful()方法会回调,并把 一些定位相关信息封装成 LocationModel 通过传参传递出来。

public interface LocationCallBackListener {

    void locationSuccessful(LocationModel locationModel);//定位成功

    void locationFailure(String msg);//定位失败

    void noPermissions();//没有定位权限
}

然后再抽取一个定位实现的接口,具体的定位方法需要实现这个接口。

public interface LocationInterface {

    void init(LocationCallBackListener listener); //定位的初始化

    void startLocation();  //开始定位

    void stopLocation(); //停止定位

    void destroyLocation();//销毁定位或一些资源释放

}

第三,新建一个类叫LocationHelper,这里面实现了对于动态权限的统一处理和对LocationInterface接口实现类的调用。
其中 PermissionHelper 权限处理工具类参考自# android M权限适配,简单工具类

/**
 * 定位辅助类 判断是否有定位权限

 * <p>
 * 使用注意 必须要调用onRequestPermissionsResult用于获取权限回调
 * <p>
 * 必须要在页面关闭时调用destroyLocation方法
 * <p>
 * Created by sx on 2017/8/7.
 */

public class LocationHelper {

    private LocationInterface mLocationInterface;//定位实现接口

    private PermissionHelper mPreHelper;//权限请求


    private LocationCallBackListener mLocationCallBackListener;//定位回调

    //需要进行定位功能的权限
    private List<String> needLocationPermissions;

    public LocationHelper(@NonNull Object obj, @NonNull LocationInterface locationInterface, @NonNull LocationCallBackListener locationCallBackListener) {
        this.mLocationInterface = locationInterface;
        this.mLocationInterface.init(locationCallBackListener);
        this.mLocationCallBackListener = locationCallBackListener;
        needLocationPermissions = new ArrayList<>();
        mPreHelper = new PermissionHelper(obj);
    }


    /**
     * 添加还需要申请的权限
     *
     * @param permissions
     */
    public void addPermissions(String permissions) {
        needLocationPermissions.add(permissions);
    }

    /**
     * 需要的定位权限
     */
    private void setLocationPermissions() {
        needLocationPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
        needLocationPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
        needLocationPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

    /**
     * 启动定位
     */
    public void startLocation() {
        if (needRequestPermiss()) {
            setLocationPermissions();
            requestPermissions();
            return;
        }
        mLocationInterface.startLocation();
    }

    /**
     * 请求定位权限
     */
    private void requestPermissions() {
        mPreHelper.requestPermissions(new PermissionHelper.PermissionListener() {
            @Override
            public void doAfterGrand(String... permission) {
                mLocationInterface.startLocation();
            }

            @Override
            public void doAfterDenied(String... permission) {
                mLocationCallBackListener.noPermissions();
            }
        }, getPermissions());
    }

    private String[] getPermissions() {
        final int size = needLocationPermissions.size();
        String[] arr = needLocationPermissions.toArray(new String[size] );
        return arr;
    }

    /**
     * 是否需要申请权限
     *
     * @return
     */
    private boolean needRequestPermiss() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
    }

    /**
     * 关闭定位
     */
    public void stopLocation() {
        mLocationInterface.stopLocation();
    }

    /**
     * 关闭一些内容
     */
    public void destroyLocation() {
        mLocationInterface.destroyLocation();
    }

    /**
     * 获取权限回调
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (mPreHelper == null) return;
        mPreHelper.handleRequestPermissionsResult(requestCode, permissions, grantResults);
    }


}

到这里我们的定位辅助工具类已经写好了,让我们来看看怎么使用。这里以高德SDK为例。

新建一个类叫GaoDeLocation并实现LocationInterface接口,其中主要实现了包括高德定位SDK的初始化、启动定位、结束等一些方法以及定位成功或失败的判断。

**
 * 高德定位
 * 
 */
public class GaoDeLocation implements LocationInterface {

    //定位功能
    protected AMapLocationClient locationClient = null;

    @Override
    public void init(final LocationCallBackListener listener) {
        //初始化client
        locationClient = new AMapLocationClient(MyApplication.getInstance());
        //设置定位参数
        locationClient.setLocationOption(getDefaultOption());
        // 设置定位监听
        locationClient.setLocationListener(new AMapLocationListener() {
            @Override
            public void onLocationChanged(AMapLocation aMapLocation) {
                if (null != aMapLocation && aMapLocation.getErrorCode() == 0) {  //定位成功
                    listener.locationSuccessful(transitionModel(aMapLocation));
                } else {
                    if (aMapLocation != null) {
                        listener.locationFailure(aMapLocation.getErrorInfo());
                        return;
                    }
                    listener.locationFailure("定位失败");
                }
                //停止定位
                stopLocation();
            }
        });
    }

    @Override
    public void startLocation() {
        locationClient.startLocation();
    }

    @Override
    public void stopLocation() {
        locationClient.stopLocation();
    }

    @Override
    public void destroyLocation() {
        if (null != locationClient) {
            locationClient.onDestroy();
            locationClient = null;
        }
    }

    /**
     * 默认定位参数
     *
     * @return
     */
    private AMapLocationClientOption getDefaultOption() {
        AMapLocationClientOption mOption = new AMapLocationClientOption();
        mOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式
        mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭
        mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效
        mOption.setInterval(2000);//可选,设置定位间隔。默认为2秒
        mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是ture
        mOption.setOnceLocation(true);//可选,设置是否单次定位。默认是false
        mOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用
        mOption.setLocationCacheEnable(false);  // 设置是否开启缓存
        AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议??裳TTP或者HTTPS。默认为HTTP
        return mOption;
    }

    /**
     * 把高德定位model转换为 自用model
     *
     * @return
     */
    private LocationModel transitionModel(AMapLocation aMapLocation) {
        LocationModel locationModel = new LocationModel();
        locationModel.setCity(aMapLocation.getCity());
        locationModel.setProvince(aMapLocation.getProvince());
        locationModel.setDistrict(aMapLocation.getDistrict());
        locationModel.setLatitude(aMapLocation.getLatitude() + "");
        locationModel.setLongitude(aMapLocation.getLongitude() + "");
        return locationModel;
    }

}

这里说明一下transitionModel() 方法,这是为了把高德SDK的定位model转为自己的model,因为不同SDK的定位model是不同,这里做一下转换,就是为了解决快速替换的问题。

**用于定位
 * Created by cdkj on 2017/11/7.
 */

public class LocationModel implements Parcelable {

    private String city;

    private String province;

    private String district;

    private String latitude;

    private String longitude;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getDistrict() {
        return district;
    }

    public void setDistrict(String district) {
        this.district = district;
    }

    public String getLatitude() {
        return latitude;
    }

    public void setLatitude(String latitude) {
        this.latitude = latitude;
    }

    public String getLongitude() {
        return longitude;
    }

    public void setLongitude(String longitude) {
        this.longitude = longitude;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.city);
        dest.writeString(this.province);
        dest.writeString(this.district);
        dest.writeString(this.latitude);
        dest.writeString(this.longitude);
    }

    public LocationModel() {
    }

    protected LocationModel(Parcel in) {
        this.city = in.readString();
        this.province = in.readString();
        this.district = in.readString();
        this.latitude = in.readString();
        this.longitude = in.readString();
    }

    public static final Creator<LocationModel> CREATOR = new Creator<LocationModel>() {
        @Override
        public LocationModel createFromParcel(Parcel source) {
            return new LocationModel(source);
        }

        @Override
        public LocationModel[] newArray(int size) {
            return new LocationModel[size];
        }
    };
}

GaoDeLocation类实现之后就可以使用LocationHelper类了。

先 new 一个 LocationHelper对象,第二个参数传入刚才的GaoDeLocation类。在需要开始定位的地方调用 mLocationHelperr.startLocation() 方法就可以进行定位了。

   LocationHelper  mLocationHelperr = new LocationHelper(MainActivity.this, new GaoDeLocation(), new LocationCallBackListener() {
            @Override
            public void locationSuccessful(LocationModel locationModel) {
                mainBinding.tvLocationInfo.setText("高德定位结果" + locationModel.getCity());
            }

            @Override
            public void locationFailure(String msg) {
                mainBinding.tvLocationInfo.setText("高德定位结果" + msg);
            }

            @Override
            public void noPermissions() {
                mainBinding.tvLocationInfo.setText("高德定位结果没有定位权限");
            }
        });

    mLocationHelperr.startLocation();

以上就实现了高德定位的过程,并且连权限管理也一并处理了,当然如果想把高德SDK替换成百度SDK也是非常简单的。我们可以把 GaoDeLocation 类 替换成 BaiDuLocation 类,这样就完成了替换。

public class BaiDuLocation implements LocationInterface {
    public LocationClient mLocationClient = null;

    @Override
    public void init(final LocationCallBackListener listener) {
        mLocationClient = new LocationClient(MyApplication.getInstance());
        mLocationClient.setLocOption(getLocationClientOption());
        //mLocationClient为第二步初始化过的LocationClient对象
        //需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用
        //更多LocationClientOption的配置,请参照类参考中LocationClientOption类的详细说明
        //声明LocationClient类
        mLocationClient.registerLocationListener(new BDAbstractLocationListener() {
            @Override
            public void onReceiveLocation(BDLocation location) {

                if (location == null) {
                    return;
                }
                int errorCode = location.getLocType();
                //获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明
                Log.i("location", errorCode + "");
                if (errorCode == 61 || errorCode == 161) {
                    listener.locationSuccessful(transitionModel(location));
                } else {
                    listener.locationFailure("定位失败");
                }
                stopLocation();
            }
        });
    }

    @NonNull
    private LocationClientOption getLocationClientOption() {
        LocationClientOption option = new LocationClientOption();

        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
//可选,设置定位模式,默认高精度
//LocationMode.Hight_Accuracy:高精度;
//LocationMode. Battery_Saving:低功耗;
//LocationMode. Device_Sensors:仅使用设备;

        option.setCoorType("bd09ll");
//可选,设置返回经纬度坐标类型,默认gcj02
//gcj02:国测局坐标;
//bd09ll:百度经纬度坐标;
//bd09:百度墨卡托坐标;
//海外地区定位,无需设置坐标类型,统一返回wgs84类型坐标

        option.setScanSpan(5000);
//可选,设置发起定位请求的间隔,int类型,单位ms
//如果设置为0,则代表单次定位,即仅定位一次,默认为0
//如果设置非0,需设置1000ms以上才有效

        option.setOpenGps(true);
//可选,设置是否使用gps,默认false
//使用高精度和仅用设备两种定位模式的,参数必须设置为true

        option.setLocationNotify(true);
//可选,设置是否当GPS有效时按照1S/1次频率输出GPS结果,默认false

        option.setIgnoreKillProcess(false);
//可选,定位SDK内部是一个service,并放到了独立进程。
//设置是否在stop的时候杀死这个进程,默认(建议)不杀死,即setIgnoreKillProcess(true)
        option.setWifiCacheTimeOut(5 * 60 * 1000);
//可选,7.2版本新增能力
//如果设置了该接口,首次启动定位时,会先判断当前WiFi是否超出有效期,若超出有效期,会先重新扫描WiFi,然后定位

        option.setEnableSimulateGps(false);

        option.setIsNeedAddress(true);

//可选,设置是否需要过滤GPS仿真结果,默认需要,即参数为false
        return option;
    }

    @Override
    public void startLocation() {
        mLocationClient.start();
    }

    @Override
    public void stopLocation() {
        mLocationClient.stop();
    }

    @Override
    public void destroyLocation() {
        mLocationClient.stop();
        mLocationClient = null;
    }

    /**
     * 把百度定位model转换为 自用model
     *
     * @return
     */
    private LocationModel transitionModel(BDLocation bdLocation) {
        LocationModel locationModel = new LocationModel();
        locationModel.setCity(bdLocation.getCity());
        locationModel.setProvince(bdLocation.getProvince());
        locationModel.setDistrict(bdLocation.getDistrict());
        locationModel.setLatitude(bdLocation.getLatitude() + "");
        locationModel.setLongitude(bdLocation.getLongitude() + "");
        return locationModel;
    }
}

当然,在使用时还有两点需要注意:

1:权限的处理回调,我们需要在页面的 onRequestPermissionsResult 方法中调用mLocationHelper的onRequestPermissionsResult() 方法,这样就实现了权限的处理。

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (mLocationHelper != null) {
            mLocationHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

2:对资源的释放,在页面结束或销毁时调用mLocationHelper.destroyLocation()方法实现一些资源的释放。

    @Override
    protected void onDestroy() {
        super.onDestroy();
         if (mLocationHelper != null) {
             mGdLocationHelper.destroyLocation();
        }
    }

以上就是定位辅助类的实现及使用。

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容