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。
相關文章
相關標籤/搜索