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);
}
很多东西虽然简单,还是有我们需要注意的地方。这就需要我们理解它们的特性了。比如上面用了弱引用来解决内存泄漏的问题,那我们就需要明白弱引用的特点,需要注意使用弱引用的变量可能为空的问题
被弱引用关联的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象
今天你进步了嘛?欢迎关注我的微信公众号,和我一起每天进步一点点!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
我要做 Android 之面笔试总结
说说什么是工厂模式 ps:之前只是单纯了解过工厂模式,不知道其实有三种工厂模式 一:工厂模式 工厂模式就有三种,它们分别是简单工厂模式(并不在23中模式之中),工厂方法模式以及抽象工厂模式,其中我们通常所说的工厂模式指的是工厂方法模式,工厂方法模式是日常开发中使用频率最高的一种设计模式。 简单工厂模式 简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。 定义: 定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。 适用场景: 其实由定义也大概能推测出其使用场景,首先由于只有一个工厂类,所以工厂类中创建的对象不能太多,否则工厂类的业务逻辑就太复杂了,其次由于工厂类封装了对象的创建过程,所以客户端应该不关心对象的创建。总结一下适用场景: (1)需要创建的对象较少。 (2)客户端不关心对象的创建过程。 以上就是简单工厂模式简单工厂模式的适用场景,下面看一个具体的实例。 实例: 创建一个可以绘制不同形状的绘图工具,可以绘制圆形,正方形,三角形,每个图形都会有一个draw()方法用于绘图,不看代码先考虑一下如何通过该模式设计完成此功能。 由题可知圆形...
-
下一篇
【朝花夕拾】Android Log篇
前言 原文:【朝花夕拾】Android Log篇 从事Android开发的这些年中,经常碰到这样一个现象:同一款app中,往往有好几种风格迥异的log处理方式,有时候会让维护者晕头转向。同时笔者也经常碰带一些模棱两可的问题:Log等级分好几种,到底什么情况下用哪个等级的log?什么情况下可以使用log,log怎么用,为什么要这么用?Android的log这么多,要怎么样高效地查看log?带着这些问题,笔者根据平时的开发经验、公司的log规范文档、网络中的相关资料,对log使用做了一定的整理。对于最基本的使用和log介绍,本文不做赘述,希望本文能帮助一部分人,也希望大牛们给出更牛的意见和建议,助我成长! 本文主要内容如下: 一、Log等级划分 Android系统为开发者提供了良好的日志工具android.util.Log,常用的方法有如下5个,将log的输出等级也划分为了5个级别: 1、Log.v:这里的v代表Verbose啰嗦的意思,对应的log等级为VERVOSE。采用该等级的log,任何消息都会输出。 2、Log.d:这里的d代表De...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 面试大杂烩
- MySQL数据库在高并发下的优化方案
- CentOS8编译安装MySQL8.0.19
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS关闭SELinux安全模块
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果