結合 Android 看看單例模式怎麼寫

定義及使用場景

定義

單例模式,就是在整個系統中某一個類的實例只有一個,而且自行實例化向整個系統提供;簡單來講,就是某個類被實例化的方式是惟一的;同時他它必須向系統自動提供這個實例。javascript

使用場景

  • 能夠避免產生多個對象消耗過多的資源,如I/O訪問等。
  • 某些類的對象就是應該只有,多個對象將致使邏輯錯誤或混亂。

常見的實現方式

下面是單例模式常見的兩種實現方式 餓漢模式和 雙重鎖模式java

  • 餓漢模式
public class HungrySingleton {

    private static HungrySingleton mInstance = new HungrySingleton();

    private HungrySingleton() {

    }

    public static HungrySingleton getInstance() {
        return mInstance;
    }
}複製代碼

不得不說,餓漢模式這個名字起得的確很巧,這種方式,無論你用不用得着這個實例,先給你建立(new)出來,生怕未來建立沒機會似得,徹底就是今朝有酒今朝醉的節奏。android

與上面對應的還有一種就是懶漢模式,就是在用的時候纔在getInstance 方法中完成實例的建立(new),真是「懶」,同時給這個方法添加synchronized 關鍵字,能夠確保在多線程狀況下單例依舊惟一,可是懶漢模式每次調用getInstance 方法時因爲synchronized 的存在,須要進行同步,形成沒必要要的資源開銷。所以便有了下面雙重鎖模式的實現方式。設計模式

  • 雙重鎖模式(DCL 實現)
public class LazySingleton {
    private static LazySingleton mInstance = null;

    private LazySingleton() {

    }

    public static LazySingleton getInstance() {
        if (mInstance == null) {
            synchronized (LazySingleton.class) {
                if (mInstance == null) {
                    mInstance = new LazySingleton();
                }
            }
        }

        return mInstance;
    }
}複製代碼

這樣既避免了餓漢模式的缺點,又解決了懶漢模式的不足;確保單例只在第一次真正須要的時候建立。安全

Android 中的使用

在平常的Android開發中,也能夠見到單例模式的身影。網絡

  • Glide
    使用Glide加載圖片很是方便,你們應該不陌生,能夠看一下它的源碼中單例模式的實現方式。
Glide.with(this).load(url).into(imageView);
    //Glide.with()
    public static RequestManager with(FragmentActivity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }
   //RequestManagerRetriever.get()
     /** The singleton instance of RequestManagerRetriever. */
    private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
     /** * Retrieves and returns the RequestManagerRetriever singleton. */
    public static RequestManagerRetriever get() {
        return INSTANCE;
    }複製代碼

能夠看到,當咱們寫下Glide.with(..) 這行代碼時,就完成了RequestManagerRetriever 這個類的實例化,這個類的單例模式是使用餓漢模式實現的。多線程

  • EventBus
public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    };複製代碼

很明顯,EventBus的單例模式使用雙重鎖模式實現的。ide

  • InputMethodManager
    static InputMethodManager sInstance
      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 的單例模式是使用懶漢模式實現。

能夠看到,關於單例模式的實現方式,面對不一樣的場景,咱們能夠作出不一樣的選擇oop

  • Glide的單例模式雖然是使用餓漢模式實現,但理論上來講並不會形成內存資源的浪費,由於當咱們經過gradle的配置引入Glide的庫時,就是爲了加載圖片,必然會使用Glide.with進行相關的操做。同時RequestManagerRetriever 這個類應該是一個網絡請求的管理類(Glide源碼沒有研究過,這裏只是猜想),這樣的一個類必然須要使用單列模式,試想若是存在多個管理類的實例,那麼談何管理,那麼的多Request到底聽哪一個manger 的,這就是前面提到必須使用單列模式的情景。gradle

  • EventBus 做爲事件總線的更要使用單例模式了,若是說EventBus的實例不是單例模式,那麼他就沒法實現它的功能了。對於EventBus不瞭解的同窗,能夠看看EventBus 3.0 相見恨晚,EventBus真的很強大。

  • InputMethodManager 使用懶漢模式實現單例也是無可厚非的,畢竟誰會去頻繁的獲取那麼多他的實例呢;同時做爲一個系統的輸入法管理器,他也必須是惟一的,所以這個類也須要單例模式來實現它惟一的實例供外部使用。

由上可見,關於單例模式的實現,沒有說哪種方式最好,只有最合適的實現方式;實際開發中,單例模式應該怎麼寫,還須要根據業務場景作最合適的選擇,不管是餓漢懶漢實用纔是好漢。我的感受,餓漢模式是一種簡單又方便的實現方式, 一個類既然已經寫成了單例模式,必然是要使用的呀,誰會去建立一個餓漢模式的單例,又不去使用這個單例呢?

以前在使用Volley的時候,就是使用餓漢模式建立整個應用的RequestQueue單例,全部須要網絡請求的地方,把request添加到RequestQueue單例中便可。

public class MyApplication extends Application{
    // 創建請求隊列
    public static RequestQueue queue;

    @Override
    public void onCreate() {
        super.onCreate();
        queue = Volley.newRequestQueue(getApplicationContext());
    }

    public static RequestQueue getHttpQueue() {
        return queue;
    }
}複製代碼

在應用Application的onCreate方法中建立了屬於整個應用的queue,以後每一次網絡請求時,只須要queue.add(Request)便可,這裏使用單例模式,能夠有效的避免在多個地方建立RequestQueue 的實例,浪費系統資源。

更多

在某些複雜的場景中,上述的兩種方式都或多或少的存在一些缺陷。所以便有了如下兩種單例模式的實現方式。

靜態內部類

public class StaticSingleton {
    private StaticSingleton(){

    }

    public static StaticSingleton getInstance(){
        return SingletonHolder.mInstance;
    }

    /** * 靜態內部類 */
    private static class SingletonHolder{
        private static final StaticSingleton mInstance=new StaticSingleton();
    }
}複製代碼

能夠說,這是最安全的實現方式了,不管怎樣,這樣產生的單例必然是單例。

枚舉單例

public enum  EnumSingleton {
    INSTANCE;
}複製代碼

定義一個枚舉元素,而他就是單例;能夠說,這是實現單例最簡單最實惠的方式;能夠有效的避免單例在反序列化的過程當中被建立,從而讓單例變得不惟一。可是,Google官方是不建議在Android開發中使用枚舉的,因此使用具體使用哪一種方式實現單例模式,仁者見仁智者見智了。

單例模式是設計模式中最簡單的一種,由於他最容易理解;但經過上述分析能夠看到,簡單不意味着隨意,針對不一樣的業務場景,須要咱們仔細斟酌單例模式的實現方式


好了,關於單例模式就是這些了。

相關文章
相關標籤/搜索