Design Patterns in Android:單例模式

前言

這是《Design Patterns in Android》系列第一篇博文,那就從本開始和筆者一起領略Android開發中設計模式的魅力吧。
本系列《設計模式Android篇》博文將遵循以下模式,循序漸進的向讀者講解設計模式在Android開發的實踐應用:

  1. 給出設計模式的定義和使用場景
  2. 給出設計模式的UML類圖
  3. 給出該設計模式的簡單Java代碼
  4. 給出該設計模式在Android源碼中的應用分析
  5. 給出該設計模式在Android應用開發中的實踐

    本文原創作者xiong_it,博客鏈接:http://blog.csdn.net/xiong_it,轉載請註明出處。

單例模式定義

單例模式(Singleton pattern):確保一個類只有一個實例,並提供對該實例的全局訪問。

根據其定義,它的使用場景:當你需要創建一個對象,但是創建這個對象時需要消耗大量的系統資源,或者這個對象迫於某種原因只能在內存中存在一個實例的時候,單例模式也許是個不錯的創建方案。

單例模式UML類圖

這裏寫圖片描述
Singleton類作爲單例類,它耦合了自身成員變量,並對外提供了一個公開方法getInstance()對instance對象的全局訪問。

單例模式代碼示例

衆所周知,單例模式有多個變種,但是最常見的還是“餓漢式”及“懶漢式”2種。本處示例代碼以線程安全的餓漢式舉例。

public class Singleton {
    private static Singleton sInstance = new Singleton();

    private Singleton() {
        super();
    }

    public static Singleton getInstance() {
        if(sInstance == null) {
            sInstance = new Singleton();
        }
        return sInstance;
    }
}

單例模式注意事項主要有3點:

  • 如果處於多線程環境,注意保持線程安全,不然就無法保證單例了
  • 單例模式的默認構造方法的修飾符需改爲private,只能類內部訪問,確保外部不能直接new出該實例
  • 單例模式需要提供一個全局訪問入口,這個入口通常以getInstance()的public靜態方法形式呈現

Android源碼中的單例模式

InputMethodManager是用來管理輸入法和軟鍵盤狀態的關鍵類,它就是源碼中一個單例模式應用的典型案例。

public final classs InputMethodManager {

    /*...
    省略代碼,保留關鍵代碼
    ...*/
    static InputMethodManager sInstance;

    InputMethodManager(IInputMethodManager service, Looper looper) {
        mService = service;
        mMainLooper = looper;
        mH = new H(looper);
        mIInputContext = new ControlledInputConnectionWrapper(looper,
                mDummyInputConnection, this);
    }

    /**
     * Retrieve the global InputMethodManager instance, creating it if it
     * doesn't already exist.
     * @hide
     */
    public static InputMethodManager getInstance() {
        synchronized (InputMethodManager.class) {
            if (sInstance == null) {
                IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
                IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
                sInstance = new InputMethodManager(service, Looper.getMainLooper());
            }
            return sInstance;
        }
    }
}

從代碼中,我們可以看到,InputMethodManager中有一個非公開的靜態成員變量sInstance,它的構造方法也是非公開的,但是它對外(framwork層)提供了一個public的靜態方法getInstance(Context)來對外提供單例對象,當該對象不存在時,就通過進程間通訊創建一個對象。
我們試想一下,假如它不是單例的話,在不同的應用中大家都可以自由創建該對象,該對象又極容易造成內存泄漏,創建N個InputMethodManager實例的話,你的Android手機該卡成什麼鬼樣子??

延伸閱讀:《Android InputMethodManager 導致的內存泄露及解決方案》

所以說,InputMethodManager做成單例是一個明智的選擇,實際上,除了InputMethodManager,直接操作系統資源的許多??Manager都是採用了單例模式來創建,比如AccessibilityManager,InputManager,LayoutInflater,BulutoothManager等。
不過他們的單例模式實現多種多樣,其中LayoutInflater及許多其他Manager是採用集合緩存的形式的實現,第一次getSystemService(String)獲取LayouInflater對象時,系統會通過ServiceFetcher創建一個對象並緩存到系統服務列表中,第二次獲取時,直接從列表中得到該對象,並不再二次創建,確保單例。

Android開發中的單例實踐

大家用過Universal-Image-Loader嗎?沒用過也沒關係,使用UIL加載一張圖片非常簡單:

ImageLoader.getInstance().displayImage(imageUrl, imageView);

很眼熟,對不對?其實,這裏ImageLoader對象的創建就是採用了單例模式的實現。
假如它不是單例實現呢?每次用都初始化一次嗎?每次都創建一個新的對象嗎?顯然這是很浪費資源的一件事,所以ImageLoader是採用了單例模式來創建一個對象,以後用的時候還是複用那個對象,保證了UIL API的易用性,同時也兼顧了系統資源的合理利用。
ImageLoader的單例實現代碼是

public class ImageLoader {
    /*
    ...
    省略代碼,保留關鍵代碼
    ...
    */
    private volatile static ImageLoader instance;

    protected ImageLoader() {
    }

    /** Returns singleton class instance */
    public static ImageLoader getInstance() {
        if (instance == null) {
            synchronized (ImageLoader.class) {
                if (instance == null) {
                    instance = new ImageLoader();
                }
            }
        }
        return instance;
    }
}
volatile關鍵字修飾的變量,一次只能有一個線程操作該變量,保證線程安全。

總結

當你的某個類多次創建很耗資源,或者你的某個類對象你只希望它存在一個實例對象在內存中時,請考慮單例模式。

好了,今天的《設計模式Android篇:單例模式》就到這裏,請繼續關注《Design Patterns in Android》(設計模式Android篇)系列博文,歡迎各位讀者朋友評論區拍磚交流,共同進步。

查看本文的最新版本,請進入:MichaelX’s Blog

發佈了69 篇原創文章 · 獲贊 251 · 訪問量 49萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章