Android设计模式之单例模式(Singleton Pattern)

我的总结学习和研究,部份内容参考《Android源码设计模式解析与实战》一书~~
 
一.  定义: 确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。
 也就是说,单例要知足3点:
       一、单例类只能有一个实例。
       二、单例类必须本身建立本身的惟一实例。(构造函数私有化,防止外部程序经过new来构造)。
       三、单例类必须给其余对象提供这一实例。(暴露公有静态方法或者经过枚举返回单例类对象)。

二.  使用场景: 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源。好比说在一个应用中,应该只有一个ImageLoader实例,这个Imageloader中含有线程池、缓存系统、网络请求等,比较消耗资源。还好比访问IO和数据库等资源,就要考虑使用单例模式。
android源码中也有不少地方用了单例模式,好比说输入法管理者InputMethodManager,好比一个应用只有一个Application对象等。
 
三. 实现方式:
 
一、饿汉式单例
特色:声明静态对象的时候进行初始化静态对象,之后再也不改变,天生是线程安全的。弊端:消耗资源。
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        retun instance;
    }
} 
 
二、懒汉式单例
特色:声明静态对象,并在第一次调用getInstance时进行初始化。优点:延迟加载。弊端:线程不安全。
public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance () {
        if (instance == null) {
            instatnce = new Singleton();
        }
        return instance;
    }
}
     
懒汉式加锁
      getInstance()静态方法添加synchronized关键字,即同步方法保证线程安全。  弊端:形成没必要要的同步开销。具体代码以下:
public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static synchronized Singleton getInstance () {
        if (instance == null) {
            instatnce = new Singleton();
        }
        return instance;
    }
}
 
三、双重检查锁定(Double CheckLock)实现单例
特色:延迟加载,解决了多余的同步,线程安全。
两次判空,第一层是为了不没必要要的同步。 第二层是为了在instance为null的状况下建立实例。
public class Singleton {
    private static Singleton instance = null;
     
    private Singleton () {
    }
     
    public static Singleton getInstance () {
        // If already initialized, no need to get lock everytime.
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance ;
    }
}

  

四、静态内部类单例
特色:延迟加载,线程安全。  利用Java虚拟机加载类的特性实现延迟加载和线程安全, 推荐使用的单例实现方式。
public class Singleton() {

    private Singleton() {
    }
    
    public static Singleton getInstance () {
        return SingletonHolder.instance;
    }
    
    /**
     * 静态内部类,只有在装载该内部类时才会去建立单例对象
     */
    private static class SingletonHolder {
         private static final Singleton instance = new Singleton();
    }

}
 
五、枚举单例
特色:写法简单,线程简单,反序列化也不会从新建立对象。(前面四种状况在反序列化中均会生成新的实例)。
 
枚举单例虽然在Effective Java中推荐使用,可是在Android平台上倒是不被推荐的。
Android官方的Training课程中明确指出:
Enums ofter require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
public enum Singleton {
    // 定义一个枚举的元素,它就是Singleton的一个实例 
    INSTANCE;

    public void doSomethig() {
        // TODO:
    }
}
 枚举在java中与普通的类同样的,不只可以有字段,还可以有本身的方法。
 最重要的是默认枚举实例的建立是线程安全的,而且在任何状况下都是一个单例。  使用方法以下:
Singleton singleton = Singleton.INSTANCE;
singleton.doSomething();
 
六、使用容器实现单例  
脑洞打开,再来看看一种另类的实现方式。维护一个统一的管理类,注入多种单例类型,在使用时根据key获取对应类型的单例对象。
public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<>();

    private Singleton() {
    }
    
    public static void addSingleton(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }

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

  

总结:  选择哪一种实现方式取决于项目自己,如是不是复杂的并发环境、JDK版本是否太低,单例对象的资源消耗等。通常而言,手机客户端一般没有高并发的状况,因此具体选择哪一种实现方式并不会有太大的影响。我的通常使用静态内部类的实现方式。
注意: 单例对象若是持有Context,很容易引起内存泄露,须要注意传递给单例对象的Context最好是Application Context。
相关文章
相关标签/搜索