Android開發(fā)中的單例模式應用詳解
本文實例講述了Android開發(fā)中的單例模式應用。分享給大家供大家參考,具體如下:
單例模式是應用最廣的設計模式之一,在應用這種模式的時候,單例對象的類必須保證只有一個實例存在。許多時候,整個系統(tǒng)只需要擁有一個全局對象,這樣有利于協(xié)調系統(tǒng)的整體行為。如一個應用中,應該只有ImageLoader實例,這個ImageLoader實例中又包含網絡請求、緩存系統(tǒng)、線程池等,很耗資源,因此沒有理由讓他構造多個實例。這種不能自由構造對象的情況就是使用單例模式的場景。在Android系統(tǒng)中存在很多這種場景,比如最常用的context.getSystemService(),BluetoothAdapter.getDefaultAdapter()等等都是使用的單例模式。下面就列出幾種單例模式的構建方式以及各種方式的優(yōu)缺點。
1.懶漢模式
懶漢模式是申明一個靜態(tài)變量,并且在用戶第一次調用getInstance時進行初始化,懶漢模式實現(xiàn)如下:
public class Singleton {
private static Singleton sInstance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(sInstance == null){
sInstance = new Singleton();
}
return sIntance;
}
}
getIntance()方法中添加了synchronized關鍵字,也就是getInstance是一個同步方法,這就是上面所說的在多線程情況下保證單例對象唯一性的手段。但是這樣存在一個問題,即使sInstance已經被初始化,每次調用getInstance都會進行同步,這樣會消耗不必要的資源,這也是懶漢單例模式存在的最大問題。懶漢模式的最大優(yōu)點是單例只有在使用的時候才會被實例化,在一定程度上節(jié)約了資源;缺點是第一次加載時需要及時初始化,反應稍慢,最大的問題是每次調用getInstance都進行同步,造成不必要的同步開銷。一般不建議使用。
2.Double Check Lock(DCL)實現(xiàn)單例
DCL方式實現(xiàn)單例模式的優(yōu)點是既能夠在需要的時候才初始化單例又能保證線程安全,且單例對象的初始化后調用getInstance不進行同步鎖。實現(xiàn)如下:
public class Singleton {
private static Singleton sInstance;
private Singleton(){}
public static Singleton getInstance(){
if(sInstance == null){
synchronized(Singleton.class){
if(sInstance == null){
sInstance = new Singleton();
}
}
}
return sInstance;
}
}
本程序的亮點在于getInstance()方法中對sIntance進行了兩次非空判斷:第一層主要是為了避免不必要的同步,第二層的判斷則是為了在null的情況下創(chuàng)建實例。假設線程A執(zhí)行到sInstance = new Singleton()的語句,這看起來是原子操作,但并不是,這句代碼會被編譯成多條匯編指令:給Singleton的實例分配內存,調用Singleton的構造函數,初始化成員字段,將sInstance對象指向分配的內存空間(此時的sInstance已經不是null了)。但是由于Java編譯器允許處理器亂序執(zhí)行,所以上述三個步驟的執(zhí)行順序無法得到保證。這就會導致DCL失效,而且這種難以跟蹤重現(xiàn)的錯誤會隱藏很久。在JDK1.5后,只需要將sInstance的定義改成private volatile static Singleton sInstance = null就可以保證sInstance對象每次都是從內存中讀取,就可以使用DCL的寫法來完成單例模式。當然,volatile或多或少會影響到性能,但考慮到程序的正確性,還是值得的。DCL的優(yōu)點是資源利用率高,第一次執(zhí)行getInstance時單例才會被實例化,效率高。缺點是第一次加載時反應稍慢,也由于Java內存模型的原因會偶爾失敗。在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生概率較小。DCL模式是使用最多的單例實現(xiàn)方式,它能夠在需要時才實例化單例,并且在絕大多數場景下保證單例對象的唯一性,除非你的代碼在并發(fā)場景比較復雜或者低于JDK6的情況下使用,否則這種方式一定能夠滿足要求。
3.靜態(tài)內部類單例模式
DCL雖然在一定程度上解決了資源消耗、多余的同步、線程安全等問題,但是它還是在某些情況下出現(xiàn)失效的問題。這個問題被才會被稱為DCL雙鎖失效。靜態(tài)內部類實現(xiàn)代碼如下:
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
當第一次加載的時候Singleton類時,并不會初始化sInstance,只有第一次在調用Singleton的getS方法時才會導致sIn被初始化。因此第一次調用get方法會導致虛擬機加載SingletonHolder類,這種方法不但保證能夠=線程安全們也能夠,也能夠保證單例對象的唯一性,同時也延遲了單例的實例化,。
4.枚舉單例模式檔
從Java1.5版本起,單元素枚舉實現(xiàn)單例模式成為最佳的方法,實現(xiàn)代碼如下
class Resource{
}
public enum SomeThing {
INSTANCE;
private Resource instance;
SomeThing() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
}
上面的類Resource是我們要應用單例模式的資源,具體可以表現(xiàn)為網絡連接,數據庫連接,線程池等等。
獲取資源的方式很簡單,只要 SomeThing.INSTANCE.getInstance() 即可獲得所要實例。下面我們來看看單例是如何被保證的:
首先,在枚舉中我們明確了構造方法限制為私有,在我們訪問枚舉實例時會執(zhí)行構造方法,同時每個枚舉實例都是static final類型的,也就表明只能被實例化一次。在調用構造方法時,我們的單例被實例化。
也就是說,因為enum中的實例被保證只會被實例化一次,所以我們的INSTANCE也被保證實例化一次。
可以看到,枚舉實現(xiàn)單例還是比較簡單的,除此之外我們再來看一下Enum這個類的聲明:
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable
可以看到,枚舉也提供了序列化機制。某些情況,比如我們要通過網絡傳輸一個數據庫連接的句柄,會提供很多幫助。
在上述的幾種單例模式中,存在一種情況它們會出現(xiàn)重復創(chuàng)建對象的情況,那就是反序列化。通過序列化可以將一個單例的實例對象寫到磁盤,然后再讀回來,從而有效的獲得一個實例。即使構造函數是私有的,反序列化時依然可以通過特殊的途徑去創(chuàng)建類的一個新的實例,相當于調用該類的私有構造函數。反序列化操作提供了一個很特別的鉤子函數,類中具有一個私有的、被實例化的方法readResolve,這個方法可以讓開發(fā)人員控制對象的反序列化。上述幾個示例中如果要杜絕對象下被反序列化中重新生成對象,那么需要加入如下方法:
private Object readResolve() throws ObjectStreamException{
return sInstance;
}
也就是在readResolve()中將sInstance對象返回,而不是默認的重新生成一個新的對象。而對于枚舉,并不存在這個問題,因為即使反序列化也不會生成新的實例。
5.使用容器實現(xiàn)單例模式
看看這種實現(xiàn)方式:
public class SingletonManager {
private static Map<String,Object> objMap = new HashMap<String,Object>();
private SingletonManager();
public static viud registerServuce(String key,Object instance){
if(!objMap.containsKey(key){
objMao.put(key,instance);
}
}
public static Object getService(String key){
return objMap.get(key);
}
}
在程序初始化的時候,將多種單例類型注入到一個統(tǒng)一的管理類中,在使用時根據Key獲取對象對應類型的獨享,這種方式使得我們可以管理多種類型的單例,并且在使用時可以通過統(tǒng)一的接口進行獲取操作,降低了用戶的使用成本,也對用戶隱藏了具體的實現(xiàn),降低了耦合度。
總結:
不管以哪種方式實現(xiàn)單例模式,它們的核心原理都是將構造函數私有化,并且通過靜態(tài)方法獲取一個唯一的實例,在獲取這個的過程中必須保證線程安全、防止反序列化導致重新生成實例對象等問題。選擇哪種方式取決于項目本身,如是否是復雜的并發(fā)環(huán)境、JDK版本過低、單例對象的資源消耗等。
更多關于Android相關內容感興趣的讀者可查看本站專題:《Android開發(fā)入門與進階教程》、《Android調試技巧與常見問題解決方法匯總》、《Android基本組件用法總結》、《Android視圖View技巧總結》、《Android布局layout技巧總結》及《Android控件用法總結》
希望本文所述對大家Android程序設計有所幫助。
相關文章
Android開發(fā)手冊自定義Switch開關按鈕控件
這篇文章主要為大家介紹了Android開發(fā)手冊自定義Switch開關按鈕控件的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
Android EditText隨輸入法一起移動并懸浮在輸入法之上的示例代碼
這篇文章主要介紹了Android EditText隨輸入法一起移動并懸浮在輸入法之上,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
Android?WindowManager深層理解view繪制實現(xiàn)流程
WindowManager是Android中一個重要的Service,是全局且唯一的。WindowManager繼承自ViewManager。WindowManager主要用來管理窗口的一些狀態(tài)、屬性、view增加、刪除、更新、窗口順序、消息收集和處理等2022-11-11

