【设计模式与Android】单例模式——独一无二的皇帝

什么是单例模式设计模式

 

所谓单例模式,就是确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例的设计模式。单例模式是最简单的设计模式,也是应用最广的设计模式。通常用于避免产生多个对象消耗过多的资源或者某种类型的对象必须独一无二的情景。安全

 

单例模式的实现方式多线程

 

(1)饿汉式并发

单例模式极其简单,仅有一个单例类。既然经常使用于确保某种类型的对象必须独一无二的情景,那么咱们能够用皇帝来举例。代码以下:框架

public class Emperor {

    
//初始化一个皇帝,国不可一日无君
    private static final Emperor emperor = new Emperor();

    
//防止刁民冒充皇帝
    private Emperor(){}

    
public static Emperor getEmperor(){
        
return emperor;
    }

}
ide

 

从上述代码中能够看到,类不能经过new的形式构造对象,只能用方法来获取惟一的静态对象。这种在声明的时候就初始化的实现方式就叫作饿汉式。oop

 

2)懒汉式性能

与饿汉式不一样,懒汉式只有在第一次调用方式时才进行初始化。实现代码以下:spa

public class Singleton {
    
    
private static Singleton instance;
    
    
private Singleton(){}
    
    
public static synchronized Singleton getInstance(){
        
if (instance == null){
            
instance = new Singleton();
        }
        
return instance;
    }
    
}
线程

 

懒汉式在方法中添加了synchronized关键字,能够在多线程状况下确保单例对象独一无二。但即便已经被初始化,每次调用还会进行同步,会消耗没必要要的资源,而且第一次加载时进行实例化会拖慢反应速度,所以懒汉式通常不建议使用。但懒汉式并不是一无可取,若是一直没有人用的话,就不会建立实例,则是节约空间。这是以时间换空间的实现方式,与饿汉式的以空间换时间各有所长。

 

还记得语文老师讲课外文学知识题的“禅杖就是鲁智深,戒刀就是武松,板斧就是李逵”的规律吗?注意上面代码的getIntance()方法,实战中见到getIntance()就是单例模式的准确率八九不离十。

 

3)DCL式

Double Check Lock(如下简称DLC)实现单例模式既可以在须要时才初始化对象,又能保证线程安全。代码以下:

public class Singleton {

    
private static Singleton instance;

    
private Singleton(){}

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

}

 

DCL能够保证不管什么时候读取这个变量,都是读到内存中最新的值,不管什么时候写这个变量,均可以当即写到内存中。DCL是目前单例模式最多见的实现方式。

4)静态内部类式

DCL尽管能完美解决资源消耗、同步多余、线程不安全的问题,却有低几率在并发场景比较复杂的状况下失效(少见于J2EE和Hadoop等场景,绝少见于Android场景)。所以在对性能要求极高的状况下咱们能够采起静态内部类式来实现单例模式。代码以下:

public class Singleton {

    
private Singleton(){}

    
public static Singleton getInstance(){
        
return SingletonHolder.instance;
    }
    
    
private static class SingletonHolder{
        
private static final Singleton instance = new Singleton();
    }

}

 

 

静态内部类式利用ClassLoader机制来保证初始化时仅有一个线程,不但不会形成性能损耗,仍是完美无缺的安全方式。

 

(5)单例模式的容器式管理

仍是拿皇帝举例子,天下可能有多个皇帝,一个软件也可能有多个单例对象。举唐玄宗李隆基和大燕皇帝安禄山的对立的例子不是太恰当,毕竟几乎没有史书认同安禄山是合法皇帝。我就举一个你们耳熟能详的例子——《三国演义》第80回讲述了中国历史上第一次同时存在两位被后世的历史学家认定为合法皇帝(曹丕和刘备)的局面,也是最著名的一次。咱们先创建一个管理类:

public class EmperorManager {

    
public static final String WEI = "魏";
    
public static final String SHU = "蜀";
    
public static final String WU = "吴";
    
    
private static Map<String,Object> emperors = new HashMap<>();

    
private EmperorManager(){}

    
public static void ascendEmperor(String key,Object emperor){
        
if (!emperors.containsKey(key)){
            
emperors.put(key,emperor);
        }
    }
    
    
public static Object getEmperor(String key){
        
return emperors.get(key);
    }

}

而后就能够管理多个单例对象了:

//曹丕废帝篡炎刘
EmperorManager.ascendEmperor(EmperorManager.WEI,WeiEmperor.getEmperor());
//汉王正位续大统
EmperorManager.ascendEmperor(EmperorManager.SHU,ShuEmperor.getEmperor());

几年后,孙权登基,天下又有了新的皇帝,写法以此类推。

 

Android源码中的单例模式

 

1)Application

Application是Android中最典型,也是最多见的单例模式。用户重写Application类也只重写一个。

 

2)Activity

Activity在singleInstance启动模式下只有一个实例,而且这个实例独立运行在一个Task中,不容许有别的Activity存在,这也是一种单例模式。

 

3)Service

Service用bindService()启动以后,不管再启动多少次,都只会调用onStartCommand()而不会再调用onCreate(),由于每次调用的Service都是同一对象。

 

4)各类Manager

Android中有不少管理类,好比WindowManager、PowerManager、SensorManager、ActivityManager、StorageManager以及ServiceManager等等,这些管理类分别对某些资源进行操做,为了不对同一资源的同时操做,也为了节约资源,都采起了单例模式。

 

5)UID

Picasso和Glide等框架流行起来以前,最多见的图片加载框架非Universal-Image-Loader(如下简称UID)莫属,UID的初始化方式以下:

//全局初始化此配置
ImageLoader.getInstance().init(config);

 

这里又见到了熟悉的getIntance(),根据思惟定式断定这是单例模式。

 

Android开发中如何利用单例模式

 

1)当建立一个对象须要较多资源时,好比读取配置或依赖较多其余对象时,能够用建立一个单例对象常驻内存的方式解决这个问题。

 

2)当一个对象须要常常调用因此须要反复建立、销毁时,为了减小内存开支,能够用单例模式来减小建立、销毁该对象的资源浪费。

 

3)当须要对同一个资源进行操做时(例如File I/O),能够建立一个FileManager,这样内存里只有一个实例,避免了对同一个资源的同时操做。

 

须要注意的几个问题

 

1)单例模式必然有static修饰符,若是持有Activity的Context,很容易形成OOM,所以尽可能使用Application的Context;此外有多少初学者在Activity销毁时忘记销毁视频或地图的单例对象而吃了大亏?

 

2)在不须要独一无二的对象的时候不要采用单例模式,譬如自定义控件就是最不适合单例模式的场景。


本系列其余博客

 

【设计模式与Android】建造者模式——建军大业


【设计模式与Android】原型模式——复制中心走出来的克隆人


【设计模式与Android】工厂方法模式——化工女神的工厂


【设计模式与Android】抽象工厂模式——嵌合体克隆工厂


【设计模式与Android】策略模式——锦囊里的上策中策下策

相关文章
相关标签/搜索