定義及使用場景
定義
單例模式,就是在整個系統中某一個類的例項只有一個,並且自行例項化向整個系統提供;簡單來說,就是某個類被例項化的方式是唯一的;同時他它必須向系統自動提供這個例項。
使用場景
- 可以避免產生多個物件消耗過多的資源,如I/O訪問等。
- 某些類的物件就是應該只有,多個物件將導致邏輯錯誤或混亂。
常見的實現方式
下面是單例模式常見的兩種實現方式 餓漢模式和 雙重鎖模式
- 餓漢模式
public class HungrySingleton {
private static HungrySingleton mInstance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return mInstance;
}
}複製程式碼
不得不說,餓漢模式這個名字起得的確很巧,這種方式,不管你用不用得著這個例項,先給你建立(new)出來,生怕將來建立沒機會似得,完全就是今朝有酒今朝醉的節奏。
與上面對應的還有一種就是懶漢模式,就是在用的時候才在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的單例模式使用雙重鎖模式實現的。
- 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 的單例模式是使用懶漢模式實現。
可以看到,關於單例模式的實現方式,面對不同的場景,我們可以做出不同的選擇
-
Glide的單例模式雖然是使用餓漢模式實現,但理論上來說並不會造成記憶體資源的浪費,因為當我們通過gradle的配置引入Glide的庫時,就是為了載入圖片,必然會使用Glide.with進行相關的操作。同時RequestManagerRetriever 這個類應該是一個網路請求的管理類(Glide原始碼沒有研究過,這裡只是猜測),這樣的一個類必然需要使用單列模式,試想如果存在多個管理類的例項,那麼談何管理,那麼的多Request到底聽哪個manger 的,這就是前面提到必須使用單列模式的情景。
-
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開發中使用列舉的,所以使用具體使用哪種方式實現單例模式,仁者見仁智者見智了。
單例模式是設計模式中最簡單的一種,因為他最容易理解;但通過上述分析可以看到,簡單不意味著隨意,針對不同的業務場景,需要我們仔細斟酌單例模式的實現方式
好了,關於單例模式就是這些了。