結合 Android 看看單例模式怎么寫
定義及使用場景
定義
單例模式,就是在整個系統中 某一個類的實例只有一個,并且自行實例化向整個系統提供 ;簡單來說,就是某個類被實例化的方式是唯一的;同時他它必須向系統自動提供這個實例。
使用場景
- 可以避免產生多個對象消耗過多的資源,如I/O訪問等。
- 某些類的對象就是應該只有,多個對象將導致邏輯錯誤或混亂。
常見的實現方式
下面是單例模式常見的兩種實現方式 餓漢模式和 雙重鎖模式
- 餓漢模式
public class HungrySingleton {
private static HungrySingleton mInstance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return mInstance;
}
}</code></pre>
不得不說, 餓漢模式 這個名字起得的確很巧,這種方式,不管你用不用得著這個實例,先給你創建(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;
}
}</code></pre>
這樣既避免了餓漢模式的缺點,又解決了懶漢模式的不足;確保單例只在第一次真正需要的時候創建。
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;
}</code></pre>
可以看到,當我們寫下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開發中使用枚舉的,所以使用具體使用哪種方式實現單例模式,仁者見仁智者見智了。
單例模式是設計模式中最簡單的一種,因為他最容易理解;但通過上述分析可以看到,簡單不意味著隨意,針對不同的業務場景,需要我們仔細斟酌單例模式的實現方式
好了,關于單例模式就是這些了。
來自:https://juejin.im/post/58e5053a2f301e0062294ba9