【設計模式與Android】單例模式——獨一無二的皇帝

什麼是單例模式設計模式

 

所謂單例模式,就是確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例的設計模式。單例模式是最簡單的設計模式,也是應用最廣的設計模式。通常用於避免產生多個對象消耗過多的資源或者某種類型的對象必須獨一無二的情景。安全

 

單例模式的實現方式多線程

 

(1)餓漢式併發

單例模式極其簡單,僅有一個單例類。既然經常使用於確保某種類型的對象必須獨一無二的情景,那麼咱們能夠用皇帝來舉例。代碼以下:框架

public class Emperor {

    
//初始化一個皇帝,國不可一日無君
    private static final Emperor emperor = new Emperor();

    
//防止刁民冒充皇帝
    private Emperor(){}

    
public static Emperor getEmperor(){
        
return emperor;
    }

}
ide

 

從上述代碼中能夠看到,類不能經過new的形式構造對象,只能用方法來獲取惟一的靜態對象。這種在聲明的時候就初始化的實現方式就叫作餓漢式。oop

 

2)懶漢式性能

與餓漢式不一樣,懶漢式只有在第一次調用方式時才進行初始化。實現代碼以下:spa

public class Singleton {
    
    
private static Singleton instance;
    
    
private Singleton(){}
    
    
public static synchronized Singleton getInstance(){
        
if (instance == null){
            
instance = new Singleton();
        }
        
return instance;
    }
    
}
線程

 

懶漢式在方法中添加了synchronized關鍵字,能夠在多線程狀況下確保單例對象獨一無二。但即便已經被初始化,每次調用還會進行同步,會消耗沒必要要的資源,而且第一次加載時進行實例化會拖慢反應速度,所以懶漢式通常不建議使用。但懶漢式並不是一無可取,若是一直沒有人用的話,就不會建立實例,則是節約空間。這是以時間換空間的實現方式,與餓漢式的以空間換時間各有所長。

 

還記得語文老師講課外文學知識題的「禪杖就是魯智深,戒刀就是武松,板斧就是李逵」的規律嗎?注意上面代碼的getIntance()方法,實戰中見到getIntance()就是單例模式的準確率八九不離十。

 

3)DCL式

Double Check Lock(如下簡稱DLC)實現單例模式既可以在須要時才初始化對象,又能保證線程安全。代碼以下:

public class Singleton {

    
private static Singleton instance;

    
private Singleton(){}

    
public static Singleton getInstance(){
        
if (instance == null){
            
synchronized (Singleton.class){
                
if (instance == null){
                    
instance = new Singleton();
                }
            }
        }
        
return instance;
    }

}

 

DCL能夠保證不管什麼時候讀取這個變量,都是讀到內存中最新的值,不管什麼時候寫這個變量,均可以當即寫到內存中。DCL是目前單例模式最多見的實現方式。

4)靜態內部類式

DCL儘管能完美解決資源消耗、同步多餘、線程不安全的問題,卻有低機率在併發場景比較複雜的狀況下失效(少見於J2EE和Hadoop等場景,絕少見於Android場景)。所以在對性能要求極高的狀況下咱們能夠採起靜態內部類式來實現單例模式。代碼以下:

public class Singleton {

    
private Singleton(){}

    
public static Singleton getInstance(){
        
return SingletonHolder.instance;
    }
    
    
private static class SingletonHolder{
        
private static final Singleton instance = new Singleton();
    }

}

 

 

靜態內部類式利用ClassLoader機制來保證初始化時僅有一個線程,不但不會形成性能損耗,仍是完美無缺的安全方式。

 

(5)單例模式的容器式管理

仍是拿皇帝舉例子,天下可能有多個皇帝,一個軟件也可能有多個單例對象。舉唐玄宗李隆基和大燕皇帝安祿山的對立的例子不是太恰當,畢竟幾乎沒有史書認同安祿山是合法皇帝。我就舉一個你們耳熟能詳的例子——《三國演義》第80回講述了中國歷史上第一次同時存在兩位被後世的歷史學家認定爲合法皇帝(曹丕和劉備)的局面,也是最著名的一次。咱們先創建一個管理類:

public class EmperorManager {

    
public static final String WEI = "魏";
    
public static final String SHU = "蜀";
    
public static final String WU = "吳";
    
    
private static Map<String,Object> emperors = new HashMap<>();

    
private EmperorManager(){}

    
public static void ascendEmperor(String key,Object emperor){
        
if (!emperors.containsKey(key)){
            
emperors.put(key,emperor);
        }
    }
    
    
public static Object getEmperor(String key){
        
return emperors.get(key);
    }

}

而後就能夠管理多個單例對象了:

//曹丕廢帝篡炎劉
EmperorManager.ascendEmperor(EmperorManager.WEI,WeiEmperor.getEmperor());
//漢王正位續大統
EmperorManager.ascendEmperor(EmperorManager.SHU,ShuEmperor.getEmperor());

幾年後,孫權登基,天下又有了新的皇帝,寫法以此類推。

 

Android源碼中的單例模式

 

1)Application

Application是Android中最典型,也是最多見的單例模式。用戶重寫Application類也只重寫一個。

 

2)Activity

Activity在singleInstance啓動模式下只有一個實例,而且這個實例獨立運行在一個Task中,不容許有別的Activity存在,這也是一種單例模式。

 

3)Service

Service用bindService()啓動以後,不管再啓動多少次,都只會調用onStartCommand()而不會再調用onCreate(),由於每次調用的Service都是同一對象。

 

4)各類Manager

Android中有不少管理類,好比WindowManager、PowerManager、SensorManager、ActivityManager、StorageManager以及ServiceManager等等,這些管理類分別對某些資源進行操做,爲了不對同一資源的同時操做,也爲了節約資源,都採起了單例模式。

 

5)UID

Picasso和Glide等框架流行起來以前,最多見的圖片加載框架非Universal-Image-Loader(如下簡稱UID)莫屬,UID的初始化方式以下:

//全局初始化此配置
ImageLoader.getInstance().init(config);

 

這裏又見到了熟悉的getIntance(),根據思惟定式斷定這是單例模式。

 

Android開發中如何利用單例模式

 

1)當建立一個對象須要較多資源時,好比讀取配置或依賴較多其餘對象時,能夠用建立一個單例對象常駐內存的方式解決這個問題。

 

2)當一個對象須要常常調用因此須要反覆建立、銷燬時,爲了減小內存開支,能夠用單例模式來減小建立、銷燬該對象的資源浪費。

 

3)當須要對同一個資源進行操做時(例如File I/O),能夠建立一個FileManager,這樣內存裏只有一個實例,避免了對同一個資源的同時操做。

 

須要注意的幾個問題

 

1)單例模式必然有static修飾符,若是持有Activity的Context,很容易形成OOM,所以儘可能使用Application的Context;此外有多少初學者在Activity銷燬時忘記銷燬視頻或地圖的單例對象而吃了大虧?

 

2)在不須要獨一無二的對象的時候不要採用單例模式,譬如自定義控件就是最不適合單例模式的場景。


本系列其餘博客

 

【設計模式與Android】建造者模式——建軍大業


【設計模式與Android】原型模式——複製中心走出來的克隆人


【設計模式與Android】工廠方法模式——化工女神的工廠


【設計模式與Android】抽象工廠模式——嵌合體克隆工廠


【設計模式與Android】策略模式——錦囊裏的上策中策下策

相關文章
相關標籤/搜索