定義及使用場景
定義
單例模式渊迁,就是在整個系統(tǒng)中某一個類的實例只有一個玉控,并且自行實例化向整個系統(tǒng)提供;簡單來說逆屡,就是某個類被實例化的方式是唯一的;同時他它必須向系統(tǒng)自動提供這個實例踱讨。
使用場景
- 可以避免產(chǎn)生多個對象消耗過多的資源魏蔗,如I/O訪問等。
- 某些類的對象就是應(yīng)該只有一個痹筛,多個對象將導(dǎo)致邏輯錯誤或混亂莺治。
常見的實現(xiàn)方式
下面是單例模式常見的兩種實現(xiàn)方式 餓漢模式和 雙重鎖模式
- 餓漢模式
public class HungrySingleton {
private static HungrySingleton mInstance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return mInstance;
}
}
不得不說,餓漢模式這個名字起得的確很巧帚稠,這種方式谣旁,不管你用不用得著這個實例,先給你創(chuàng)建(new)出來滋早,生怕將來創(chuàng)建沒機會似得榄审,完全就是今朝有酒今朝醉的節(jié)奏。
與上面對應(yīng)的還有一種就是懶漢模式杆麸,就是在用的時候才在getInstance 方法中完成實例的創(chuàng)建(new)搁进,真是“懶”浪感,同時給這個方法添加synchronized 關(guān)鍵字,可以確保在多線程情況下單例依舊唯一饼问,但是懶漢模式每次調(diào)用getInstance 方法時由于synchronized 的存在篮撑,需要進(jìn)行同步,造成不必要的資源開銷匆瓜。因此便有了下面雙重鎖模式的實現(xiàn)方式。
- 雙重鎖模式(DCL 實現(xiàn))
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;
}
}
這樣既避免了餓漢模式的缺點未蝌,又解決了懶漢模式的不足驮吱;確保單例只在第一次真正需要的時候創(chuàng)建。
Android 中的使用
在日常的Android開發(fā)中萧吠,也可以見到單例模式的身影左冬。
- Glide
使用Glide加載圖片非常方便,大家應(yīng)該不陌生纸型,可以看一下它的源碼中單例模式的實現(xiàn)方式拇砰。
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;
}
可以看到,當(dāng)我們寫下Glide.with(..) 這行代碼時狰腌,就完成了RequestManagerRetriever 這個類的實例化除破,這個類的單例模式是使用餓漢模式實現(xiàn)的。
- EventBus
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
};
很明顯琼腔,EventBus的單例模式使用雙重鎖模式實現(xiàn)的瑰枫。
- 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 的單例模式是使用懶漢模式實現(xiàn)。
可以看到丹莲,關(guān)于單例模式的實現(xiàn)方式光坝,面對不同的場景,我們可以做出不同的選擇
Glide的單例模式雖然是使用餓漢模式實現(xiàn)甥材,但理論上來說并不會造成內(nèi)存資源的浪費盯另,因為當(dāng)我們通過gradle的配置引入Glide的庫時,就是為了加載圖片洲赵,必然會使用Glide.with進(jìn)行相關(guān)的操作鸳惯。同時RequestManagerRetriever 這個類應(yīng)該是一個網(wǎng)絡(luò)請求的管理類(Glide源碼沒有研究過,這里只是猜測)板鬓,這樣的一個類必然需要使用單列模式悲敷,試想如果存在多個管理類的實例,那么談何管理俭令,那么的多Request到底聽哪個manger 的后德,這就是前面提到必須使用單列模式的情景。
EventBus 作為事件總線的更要使用單例模式了抄腔,如果說EventBus的實例不是單例模式瓢湃,那么他就無法實現(xiàn)它的功能了理张。對于EventBus不了解的同學(xué),可以看看
EventBus 3.0 相見恨晚绵患,EventBus真的很強大雾叭。InputMethodManager 使用懶漢模式實現(xiàn)單例也是無可厚非的,畢竟誰會去頻繁的獲取那么多他的實例呢落蝙;同時作為一個系統(tǒng)的輸入法管理器织狐,他也必須是唯一的,因此這個類也需要單例模式來實現(xiàn)它唯一的實例供外部使用筏勒。
由上可見移迫,關(guān)于單例模式的實現(xiàn),沒有說哪一種方式最好管行,只有最合適的實現(xiàn)方式厨埋;實際開發(fā)中,單例模式應(yīng)該怎么寫捐顷,還需要根據(jù)業(yè)務(wù)場景做最合適的選擇荡陷,無論是餓漢懶漢實用才是好漢。個人感覺迅涮,餓漢模式是一種簡單又方便的實現(xiàn)方式废赞, 一個類既然已經(jīng)寫成了單例模式,必然是要使用的呀叮姑,誰會去創(chuàng)建一個餓漢模式的單例蛹头,又不去使用這個單例呢?
之前在使用Volley的時候戏溺,就是使用餓漢模式創(chuàng)建整個應(yīng)用的RequestQueue單例渣蜗,所有需要網(wǎng)絡(luò)請求的地方,把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;
}
}
在應(yīng)用Application的onCreate方法中創(chuàng)建了屬于整個應(yīng)用的queue耕拷,之后每一次網(wǎng)絡(luò)請求時,只需要queue.add(Request)即可托享,這里使用單例模式骚烧,可以有效的避免在多個地方創(chuàng)建RequestQueue 的實例,浪費系統(tǒng)資源闰围。
更多
在某些復(fù)雜的場景中赃绊,上述的兩種方式都或多或少的存在一些缺陷。因此便有了以下兩種單例模式的實現(xiàn)方式羡榴。
靜態(tài)內(nèi)部類
public class StaticSingleton {
private StaticSingleton(){
}
public static StaticSingleton getInstance(){
return SingletonHolder.mInstance;
}
/**
* 靜態(tài)內(nèi)部類
*/
private static class SingletonHolder{
private static final StaticSingleton mInstance=new StaticSingleton();
}
}
可以說碧查,這是最安全的實現(xiàn)方式了,無論怎樣,這樣產(chǎn)生的單例必然是單例忠售。
枚舉單例
public enum EnumSingleton {
INSTANCE;
}
定義一個枚舉元素传惠,而他就是單例;可以說稻扬,這是實現(xiàn)單例最簡單最實惠的方式卦方;可以有效的避免單例在反序列化的過程中被創(chuàng)建,從而讓單例變得不唯一泰佳。但是盼砍,Google官方是不建議在Android開發(fā)中使用枚舉的,所以使用具體使用哪種方式實現(xiàn)單例模式逝她,仁者見仁智者見智了衬廷。
單例模式是設(shè)計模式中最簡單的一種,因為他最容易理解汽绢;但通過上述分析可以看到,簡單不意味著隨意侧戴,針對不同的業(yè)務(wù)場景宁昭,需要我們仔細(xì)斟酌單例模式的實現(xiàn)方式
好了,關(guān)于單例模式就是這些了酗宋。