Android 单利模式-双重锁

单利模式:

        是一种常用的软件设计模式,在它的核心结构中值包含一个被称为单例的特殊类。一个类只有一个实例,即一个类只有一个对象实例。

        例如:本人在开发过程中遇到一个问题,集成高德地图,因逻辑需要,需要实时开启定位和实时关闭定位,所以将高德地图的开启定位和关闭定位,抽成一个工具类来使用。点击按钮调用工具类开启定位,在别的界面想要关闭这个定位,再次调用工具类发现关闭不了。代码如下:

public class LocalUtils {
    private Context mContext;
    private AMapLocationClient locationClient = null;
    private AMapLocationClientOption locationOption = null;
    public static double longitude = 0;
    public static double latitude = 0;
    private boolean isRefreshAnim = false; // 是否在执行刷新动画
    private OnItemClickListener mOnItemClickListener = null;
    public LocalUtils(Context context) {
        this.mContext = context;
    }

    public void startLocation() {
        //初始化client
        locationClient = new AMapLocationClient(mContext);
        locationOption = getDefaultOption();
        //设置定位参数
        locationClient.setLocationOption(locationOption);
        // 设置定位监听
        locationClient.setLocationListener(locationListener);
        // 设置定位参数
        locationClient.setLocationOption(locationOption);
        // 启动定位
        locationClient.startLocation();
    }
    public void stopLocation(){
        if (locationClient!=null){
            //销毁定位客户端,同时销毁本地定位服务。
            locationClient.stopLocation();
            locationClient.onDestroy();
        }
    }

    private AMapLocationClientOption getDefaultOption() {
        AMapLocationClientOption mOption = new AMapLocationClientOption();
        //如果网络可用就选择高精度
        if (NetworkUtil.isAvailable(mContext)) {

            mOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式
            mOption.setGpsFirst(true);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭
        }
        //否则就选择仅设备模式
        else {
            mOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式
            mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭
        }
        mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效
        mOption.setInterval(2000);//可选,设置定位间隔。默认为2秒
        mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是true
        mOption.setOnceLocation(false);//可选,设置是否单次定位。默认是false
        mOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用
        AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP
        mOption.setSensorEnable(true);//可选,设置是否使用传感器。默认是false
        mOption.setWifiScan(true); //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差
        mOption.setLocationCacheEnable(true); //可选,设置是否使用缓存定位,默认为true
        return mOption;
    }

    AMapLocationListener locationListener = new AMapLocationListener() {
        @Override
        public void onLocationChanged(AMapLocation location) {
            StringBuilder sb = new StringBuilder();
            if (null != location) {
                //errCode等于0代表定位成功,其他的为定位失败,具体的可以参照官网定位错误码说明
                if (location.getErrorCode() == 0) {
                    if (isRefreshAnim) {
                        ToastUtils.showToast("获取实时位置成功!");
                    }

                    longitude = location.getLongitude();
                    latitude = location.getLatitude();
                    String address = location.getAddress();
                    locationSuccess(longitude, latitude, location, address);
                    //定位完成的时间

                } else {
                    //定位失败
                    sb.append("定位失败" + "\n");
                    sb.append("错误码:" + location.getErrorCode() + "\n");
                    sb.append("错误信息:" + location.getErrorInfo() + "\n");
                    sb.append("错误描述:" + location.getLocationDetail() + "\n");
                    LocationFarile("获取失败", location);
                    if (isRefreshAnim) {
                        ToastUtils.showToast("获取实时位置失败!");
                    }
                }
            } else {
                LocationFarile("获取失败", location);

                if (isRefreshAnim) {
                    ToastUtils.showToast("获取实时位置失败!");
                }
            }
        }
    };

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }


    private void LocationFarile(String count, AMapLocation location) {
        if (mOnItemClickListener != null) {
            mOnItemClickListener.onItemClick(0, 0, location, count, "");
        }
    }

    //定位
    public void locationSuccess(double longitude, double latitude, AMapLocation location, String address) {
        if (mOnItemClickListener != null) {
            mOnItemClickListener.onItemClick(longitude, latitude, location, "", address);
        }
    }

    public interface OnItemClickListener {
        void onItemClick(double longitude, double latitude, AMapLocation location, String count, String address);

    }
}

//调用方法

 LocalUtil localUtil = new LocalUtil(getActivity());
            localUtil.startLocation();
            localUtil.setOnItemClickListener(new LocalUtil.OnItemClickListener() {
                @Override
                public void onItemClick(double longitude, double latitude, AMapLocation location, String count, String address) {
                    textEdit.setText(longitude + "");
                    item.setDisplayValue(longitude + "");
                }
            });

原因:再次new 对象 会生成新的对象(对象在内存中指向地址不同),所以无法关闭之前对象里面的定位。

解决办法

把该工具类弄成单利的,这时候这个类只有一个实例,内存地址不会改变(加上同步锁,防止改变)。代码如下:增加单例

private static Context mContext;
    private static volatile LocalUtils localUtils=null;
    private LocalUtils(Context mContext){
        this.mContext=mContext;
    }
    public static LocalUtils getInstance(Context mContext){
        if (localUtils==null){
            synchronized (LocalUtils.class){
                if (localUtils==null){
                    localUtils=new LocalUtils(mContext);
                }
            }
        }
        return localUtils;
    }

//调用方法

LocalUtils.getInstance(getApplicationContext()).startLocation();
                LocalUtils.getInstance(getApplicationContext()).setOnItemClickListener(new LocalUtils.OnItemClickListener() {
                    @Override
                    public void onItemClick(double longitude, double latitude, AMapLocation location, String count, String address) {
                        
                    }
                });

到这问题基本已经解决了,调用代码如下:停止定位

LocalUtils.getInstance(getApplicationContext()).startLocation();

当我们调用工具类的getInstance方法时,它会判断当前对象是否存在,如果已存在,不会再次创建,所以这时候拿到的是之前的对象,关闭定位是没一点问题的。

单例(参考文章)

        常见的几种单例模式   https://www.cnblogs.com/Ycheng/p/7169381.html

        单例模式-双重校验锁   https://github.com/Dazhi528/libBluetooth

        Android 单例模式(懒汉式+饿汉式+双重锁也就是同步锁)  https://blog.csdn.net/qq_42545144/article/details/87946551

        单例模式-双重校验锁   https://www.jianshu.com/p/829a523c32aa

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值