Android設計模式之單例模式(Singleton Pattern)


個人總結學習和研究,部分內容參考《Android源碼設計模式解析與實戰》一書~~
 
一.  定義: 確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。
 也就是說,單例要滿足3點:
       1、單例類只能有一個實例。
       2、單例類必須自己創建自己的唯一實例。(構造函數私有化,防止外部程序通過new來構造)。
       3、單例類必須給其他對象提供這一實例。(暴露公有靜態方法或者通過枚舉返回單例類對象)。

二.  使用場景: 確保某個類有且只有一個對象的場景,避免產生多個對象消耗過多的資源。比如說在一個應用中,應該只有一個ImageLoader實例,這個Imageloader中含有線程池、緩存系統、網絡請求等,比較消耗資源。還比如訪問IO和數據庫等資源,就要考慮使用單例模式。
android源碼中也有很多地方用了單例模式,比如說輸入法管理者InputMethodManager,比如一個應用只有一個Application對象等。
 
三. 實現方式:
 
1、餓漢式單例
特點:聲明靜態對象的時候進行初始化靜態對象,以后不再改變,天生是線程安全的。弊端:消耗資源。
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        retun instance;
    }
} 
 
2、懶漢式單例
特點:聲明靜態對象,並在第一次調用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;
    }
}
 
3、雙重檢查鎖定(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 ;
    }
}

  

4、靜態內部類單例
特點:延遲加載,線程安全。  利用Java虛擬機加載類的特性實現延遲加載和線程安全, 推薦使用的單例實現方式。
public class Singleton() {

    private Singleton() {
    }
    
    public static Singleton getInstance () {
        return SingletonHolder.instance;
    }
    
    /**
     * 靜態內部類,只有在裝載該內部類時才會去創建單例對象
     */
    private static class SingletonHolder {
         private static final Singleton instance = new Singleton();
    }

}
 
5、枚舉單例
特點:寫法簡單,線程簡單,反序列化也不會重新創建對象。(前面四種情況在反序列化中均會生成新的實例)。
 
枚舉單例雖然在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();
 
6、使用容器實現單例  
腦洞打開,再來看看一種另類的實現方式。維護一個統一的管理類,注入多種單例類型,在使用時根據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。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM