Android的单例模式的N种实现方式

说起Androidd的23种设计模式,恐怕大多数人不能说全,但是说起单例模式,大多数人不管是初级开发还是资深开发工程师都应该知道并且使用过。单例模式应该是最简单也是比较常用的设计模式了。但是你真的了解单例模式吗?最近看了《Android源码设计模式》这本书,才发现我原来理解的单例模式太冰山一角了。今天就书中谈到的单例模式做一下总结吧。

单例模式的定义及使用场景

定义:确保某个类只有一个实例,而且自行实例化提供给外部使用。

使用场景:某个类型的对象只应该有且只有一个,或者避免创建多个对象消耗过多的资源时。如:访问IO或数据库时要考虑单例模式。

N种实现方式及比较 

饿汉式

public class SingleTon {
    //将构造函数私有化
    private SingleTon() {
    }

    //创建私有实例对象
    private static final SingleTon singleTonInstance = new SingleTon();

    //对外提供方法,返回实例对象
    public static SingleTon getInstance() {
        return singleTonInstance;
    }
}


优点:简单,线程安全。

缺点:实例对象是static的,在声明的时候就实例化了,浪费资源。

懒汉式

public class SingleTon {
    //声明私有化
    private static SingleTon singleTonInstance;
    //将构造函数私有化
    private SingleTon() {
    }
    //懒汉式
    private static synchronized SingleTon getInstance(){
        if (null==singleTonInstance){
            singleTonInstance = new SingleTon();
        }
        return singleTonInstance;
    }
}


优点:用到的时候才会去实例化,在一定程度上节约了资源。

缺点:getInstance方法是用synchronized修饰的,该方法是同步的,为了保证线程安全,但是导致每次调用该方法的时候都会被同步,这样会消耗不必要的资源(不必要的同步开销)。所以这种模式一般不建议使用。

Double Check Lock(DCL模式):双重检查锁定

public class SingleTon {
    //声明私有化
    private static SingleTon singleTonInstance;
    //将构造函数私有化
    private SingleTon() {
    }
    //Double Check Lock
    public static SingleTon getInstance(){
        if (singleTonInstance==null){
            synchronized (SingleTon.class){
                if (singleTonInstance==null){
                    singleTonInstance = new SingleTon();
                }
            }
        }
        return singleTonInstance;
    }
}


可以看到getInstance()方法对singleTonInstance进行两次判空,对懒汉式进行了优化,只有在第一次实例化的时候才会走第二个分支,才会同步,避免了每次都同步造成的不必要的资源消耗。

优点:第一次执行getInstance方法时才会实例化,资源利用率高,效率高。

缺点:偶尔失效(高并发条件下,由于JDK版本问题,在jdk1.5之前会失败)

静态内部类实现

public class SingleTon {
    //将构造函数私有化
    private SingleTon() {
    }

    public static SingleTon getInstance() {
        return SingleTonHoulder.singleTonInstance;
    }

    //静态内部类
    public static class SingleTonHoulder {
        private static final SingleTon singleTonInstance = new SingleTon();
    }
}


第一次调用getInstance()方法的时候,虚拟机才会加载SingleTonHoulder静态内部类

优点:线程安全,保证单例的唯一性,延迟了对象的实例化,是推荐的方式。

缺点:

枚举

以上的单例实现方法都没有考虑一个因素:反序列化,即使构造函数是私有的,反序列化仍然有特殊的途径去创建类的一个新的实例。但是同构枚举实现单例不会有这样的问题,因为枚举提供了序列化机制。

public enum SingleTon {
    INSTANCE;
    
}

具体这点我还不是太理解。以后理解了再分享吧。

使用容器实现单例

public class SingleTonManager {
    private static Map objMap = new HashMap<>();

    private SingleTonManager() {
    }

    public static void registerService(String key, Object object) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, object);
        }
    }

    public static Object getService(String key) {
        return objMap.get(key);
    }
}


将多种单例类型注入到一个统一的管理类中,使用时根据key获取对应的对象,这种模式使得我们可以和管理多种类型的单例,并且在使用的时候可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏的具体的实现,降低了耦合度。

Android源码中单例使用

LayoutInflater.from(Context context);

EventBus.getDefault()

ImageLoader.getInstance();

......

他们实现单例的方式不同,大家可以去看源码

总结

单例模式是使用频率较高的设计模式,但是由于客户端通常没有高并发的情款,选择哪种实现方式并不会有太大影响。但是出于效率考虑,推荐使用“DCL”和“静态内部类”实现方式。

单例模式的优点:

1.在内存中只有一个实例,减少内存开支

2.只生产一个实例,减少系统性能的性能开销

3.避免对资源的多重占用。

4.可以在系统设置全局的访问点,优化和共享资源访问。(例如可以设置一个单例类,负责所有数据表的映射处理)

单例的缺点;

1.单例一般没有接口,扩展很困难。

2.单例如果持有Context对象,很容易引起内存泄漏,最好传递全局的Application Context。




你可能感兴趣的:(Android)