單例設計模式是應用最廣的,它必須保證一個實例,例如一個ImageLoader實例,自行實例化并且向整個系統提供這個實例。
當創建一個對象的時候消耗過多,例如要訪問IO、數據庫資源等時使用。
特點
1.構造函數不對外開放,private。
2.通過一個靜態方法或者枚舉返回單例類對象。
3.確保單例對象只有一個,特別是多線程。
4.確保在反序列化時不會重新構建對象
分為兩種:
餓漢:類一創建就new對象出來。
懶漢:使用時候才去通過判斷對象是否為null來new新對象。
為了保證對象唯一,有幾種實現方法:
1.懶漢DCL(DoubleCheckLock)(一般能滿足需求)
雙重校驗同步代碼塊方法去new對象。
缺點:
高并發下,由于不是原子操作,可能會失效,
java5以后,可以在成員變量上加上volatile來禁止該對象上的讀寫指令重排序。
2.靜態內部類單例模式: 推薦
3.枚舉單例 避免發序列化產生對象另類:容器實現單例模式
缺點:
接口擴展困難,
如果傳入的是Activity的context 很可能內存泄露
4.使用容器來實現單例。
android源碼中的單例模式:
?我們常常通過context來獲取系統級別的服務,如WindowsManagerService、ActivityManagerService等,跟常用的是LayoutInflater類。
下面來分析下LayoutInflater
LayoutInflater.form(context)獲取單例->點進去也是調用了context.getSystemService(context.LAYOUT_INFLATER_SERVICE)
我們知道Activity對象和Context對象是在ActivityThread的main函數中生成的,找到main函數發現context是ComtextImpl.createActivityContext(this,r.pakageInfo,t.token)生成,然后通過activity.Attach傳入Activity中的。
最后來看ComtextImpl類
這里使用的使用容器實現單例模式。
容器:private static final HashMap<String,ServiceFetcher> SYSTEM_SERVICE_MAP = new ...;
//在ComtextImpl類static代碼塊中,會生成各種ServiceFatcher,然后存入SYSTEM_SERVICE_MAP中。
ComtextImpl類有一個getSystemService(String name)方法用來獲取存入SYSTEM_SERVICE_MAP中的ServiceFatcher對象,然后調用fetcher.getServiece(comtextImpl)方法。
fetcher中的getServiece方法從緩存ctxImpl.mServiceCache中獲取對象cache.get(mContextCacheIndex)避免重復創建。
我們如何運用單例模式
例子:
ImageLoader使用DCL(Double checkLock)的方式來獲取對象
public static InmageLoadegetInstance(){
?if(sInstance ==null){
? ? synchronized(ImageLoader.class){
? ? ? i f(sInstance ==null){
? ? ? ? sInstance = new ImageLoader();
? ? ? }
? ? }
? }
}