Android 單例模式

源碼地址
說明:

此筆記是在看完 Android 源碼設計模式解析與實戰 中單例模式進行的總結。

使用場景

確保某個類有且只有一個對象,避免產生多個對象消耗過多的資源。
例如:創建一個對象需要消耗的資源過多,如IO、數據庫操作等。

實現關鍵點

  1. 私有化構造函數;
  2. 通過一個靜態方法或者枚舉返回單例類對象;
  3. 確保單例類對象有且只有一個,尤其在多線程環境下;
  4. 確保單例類對象在反序列化時不會重新構建對象。

6種創建模式

  1. 餓漢模式

    特點:

    • 單例類加載的時候就已經初始化;
    • 由于在類加載的時候就創建實例對象,比較消耗資源,但是第一次加載時較快。

    示例:

    public class Singleton {
       private static Singleton sInstance = new Singleton();
       private Singleton() { }
       
       public static Singleton getInstance() {
           return sInstance; 
       }
    }
    
  2. 懶漢模式

    特點:

    • 只有在使用時才初始化實例,在一定程度上節約了資源;
    • 第一次加載時需要進行實例化,反應稍慢;
    • 每次調用 getInstance 都進行同步,造成不必要的開銷。

    示例:

    public class Singleton {
       private static Singleton sInstance;
       private Singleton() { }
       
       public static synchronized Singleton getInstance() {
          if(sInstance == null) {
             sInstance = new Singleton(); 
          }
          return sInstance; 
       }
    }
    
  3. Double Check Lock (DCL)模式

    特點:

    • 資源利用率高,第一次執行 getInstance 時單例對象才會被實例化,效率高,并且線程安全;
    • 第一次加載反應稍慢,在高并發狀態下有一定的缺陷。可以使用關鍵字 volatile 進行優化。如:private volatile static Singleton instance;

    示例:

    public class Singleton {
       private static Singleton sInstance;
       private Singleton() { }
       
       public static Singleton getInstance() {
          if(sInstance == null) {
             synchronized (Singleton.class) {
                if (sInstance == null) {
                   sInstance = new Singleton(); 
                }         
             }
          }
          return sInstance; 
       }
    }
    
  4. 靜態內部類單例

    特點:

    • 只有在第一次調用 Singleton 的 getInstance 方法時才會實例化;
    • 線程安全,可以確保單例的唯一性。

    示例:

    public class Singleton {  
       private Singleton() { }
       
       public static Singleton getInstance() {
           return SingletonHolder.sInstance; 
       }
      
       /** 靜態內部類 */
       private static class SingletonHolder {
          private static Singleton sInstance = new Singleton();
       }
    }
    
  5. 枚舉單例

    特點:

    • 寫法簡單,線程安全;

    • 上面的幾種寫法,反序列化會出現重新創建對象的情況,而枚舉不會;

    • 如果需要杜絕這個問題,需要在類中創建一個方法,寫法如下:

      private Object readRessolve() throws ObjectStreamException {
         return sInstance;
      }
      

    示例:

    public enum SingletonEnum {
       INSTANCE;
       public void doSomething() {
          //doSomething... 
       }
    }
    

    ?

  6. 使用容器實現單例

    特點:

    • 在程序的初始,將多種單例類型注入到一個統一的管理類中,使用時根據 key 獲取對應類型的對象;
    • 方便管理多種類型的單例。

    示例:

    public class SingletonManager {
       private static Map<String, Object> objMap = new HashMap<>();
       
       private SingletonManager() { }
       public static void registerService (String key, Object instance) {
          if (!objMap.containsKey(key)) {
             objMap.put(key, instance);
          } 
       }
      
       public static Object getService(String key) {
          return objMap.get(key); 
       }
    }
    

Android 源碼中的單例

LayoutInflater

詳情參見 Android 源碼設計模式解析與實戰

總結

  • 優點
    1. 由于單例模式中內存只有一個實例,減少了內存開支,特別對于一個對象需要頻繁地創建、銷毀時,優勢比較明顯;
    2. 當創建一個對象需要較多的資源時,可以使用單例解決;
    3. 單例模式可以避免對資源的多重占用。例如一個寫文件操作,由于只有一個實例存在內存中,避免對同一個資源文件的同時寫操作。
    4. 單例模式可以在系統設置全局訪問點,優化和共享資源訪問。例如,可以設計一個單例類,負責所有數據表的映射處理。
  • 缺點
    1. 單例模式一般沒有借口,擴展很困難。若要擴展,需要在源代碼上進行修改。
    2. 單例對象如果持有 Context ,那么很容易引發內存泄漏,此時需要注意傳遞給單例對象 Application Context。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容