Android 单例模式的正确姿势

简介:

单例模式是使用得最多的设计模式,模版代码也很多。但是如果使用不当还是容易出问题。

DCL模式(双重检查锁定模式)的正确使用方式

一般我们使用DCL方法来实现单例模式时都是这样的模版代码:

private static Singleton mSingleton = null;
private Singleton () {}

public static Singleton getInstance() {
    if (mSingleton == null) {
        synchronized (Singleton.class) {
            if (mSingleton == null) {
                mSingleton = new Singleton();
            }
        }
    }
    return mSingleton;

}

实际上,上述方法在多线程的环境下,还是会有可能创建多个实例。为什么呢?

mConstant = new Constant()这行代码虚拟机在执行的时候会有多个操作,大致包括:

  • 为新的对象分配内存
  • 调用Constant的构造方法,初始化成员变量
  • 将mConstant这个引用指向新创建的Constant对象的地址

在多线程环境下,每个线程的私有内存空间中都有mSingleton的副本。这导致可能存在下面的情况:

  • 当在一个线程中初始化mSingleton后,主内存中的mSingleton变量的值可能并没有及时更新;
  • 主内存的mSingleton变量已经更新了,但在另一个线程中的mSingleton变量没有即时从主内存中读取最新的值

这样的话就有可能创建多个实例,虽然这种几率比较小。

那怎么解决这个问题呢?答案是使用volatile关键字

volatile关键字能够保证可见性,被volatile修饰的变量,在一个线程中被改变时会立刻同步到主内存中,而另一个线程在操作这个变量时都会先从主内存更新这个变量的值。

更保险的单例模式实现

private volatile static Singleton mSingleton = null;
private Singleton () {}

public static Singleton getInstance() {
    if (mSingleton == null) {
        synchronized (Singleton.class) {
            if (mSingleton == null) {
                mSingleton = new Singleton();
            }
        }
    }
    return mSingleton;

}

使用单例模式,小心内存泄漏了喔~

单例模式的静态特性导致它的对象的生命周期是和应用一样的,如果不注意这一点就可能导致内存泄漏。下面看看常见的2种情况

  • Context的泄漏
//SingleInstance.class
private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}

public static SingleInstance getInstance(Context context) {
    if (mSingleInstance == null) {
        synchronized (SingleInstance.class) {
            if (mSingleInstance == null) {
                mSingleInstance = new SingleInstance(context);
            }
        }
    }
    return mSingleInstance;

}

//MyActivity
public class MyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //这样就容易出问题了
        SingleInstance singleInstance = SingleInstance.getInstance(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

如上面那样直接传入MyActivity的引用,如果当前MyActivity退出了,但应用还没有退出,singleInstance一直持有MyActivity的引用,MyActivity就不能被回收了。

解决方法也很简单,传入ApplicationContext就可以了。

SingleInstance singleInstance = SingleInstance.getInstance(getApplicationContext());
  • View的泄漏

如果单例模式的类中有跟View相关的属性,就需要注意了。搞不好也会导致内存泄漏,原因和上面分析的原因一样。

//SingleInstance.class
private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}

public static SingleInstance getInstance(Context context) {
    if (mSingleInstance == null) {
        synchronized (SingleInstance.class) {
            if (mSingleInstance == null) {
                mSingleInstance = new SingleInstance(context);
            }
        }
    }
    return mSingleInstance;

}

//单例模式中这样持有View的引用会导致内存泄漏
private View myView = null;
public void setMyView(View myView) {
    this.myView = myView;
}

解决方案是采用弱引用

private volatile static SingleInstance mSingleInstance = null;
private SingleInstance (Context context) {}

public static SingleInstance getInstance(Context context) {
    if (mSingleInstance == null) {
        synchronized (SingleInstance.class) {
            if (mSingleInstance == null) {
                mSingleInstance = new SingleInstance(context);
            }
        }
    }
    return mSingleInstance;

}

//    private View myView = null;
//    public void setMyView(View myView) {
//        this.myView = myView;
//    }

//用弱引用
private WeakReference<View> myView = null;
public void setMyView(View myView) {
    this.myView = new WeakReference<View>(myView);
}

很多东西虽然简单,还是有我们需要注意的地方。这就需要我们理解它们的特性了。比如上面用了弱引用来解决内存泄漏的问题,那我们就需要明白弱引用的特点,需要注意使用弱引用的变量可能为空的问题

被弱引用关联的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象


今天你进步了嘛?欢迎关注我的微信公众号,和我一起每天进步一点点!
AntDream

目录
相关文章
|
设计模式 SQL 缓存
Android 单例模式必知必会
一、概念 单例模式是运用最广泛的设计模式之一,在应用这个模式时,单例模式的类必须保证只有一个实例存在。多用于整个程序只需要有一个实例,通常很消耗资源的类,比如线程池,缓存,网络请求,IO操作,访问数据库等。由于类比较耗资源,所以没必要让它构造多个实例,这种就是单例模式比较好的使用场景。
219 0
|
安全 Java Android开发
我要做 Android 之单例模式
Q:实现单例模式有几种方法?懒汉式中双层锁的目的是什么?两次判空的目的又是什么? 懒汉式(线程不安全) 单例模式最后的目的无非就是获取当前存在的实例对象,如果没有实例对象就实例化一个,有就直接返回。
1210 0
|
安全 Java 缓存
Android--单例模式
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/chaoyu168/article/details/78897883 ###定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
841 0
|
API Android开发
Android设计模式系列-单例模式
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/chaoyu168/article/details/52592563 单例模式,可以说是GOF的23种设计模式中最简单的一个。
933 0
|
API 开发工具 Android开发
Android设计模式系列(3)--SDK源码之单例模式
原文链接:http://www.cnblogs.com/qianxudetianxia/archive/2011/08/07/2130306.html 单例模式,可以说是GOF的23种设计模式中最简单的一个。
1007 0
|
1月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
19天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
1月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
19天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
45 14
|
22天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。