結(jié)合Android看看單例模式怎么寫

定義及使用場景

定義

單例模式渊迁,就是在整個系統(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)于單例模式就是這些了酗宋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末积仗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜕猫,更是在濱河造成了極大的恐慌寂曹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件回右,死亡現(xiàn)場離奇詭異隆圆,居然都是意外死亡,警方通過查閱死者的電腦和手機翔烁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門渺氧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹬屹,你說我怎么就攤上這事侣背。” “怎么了慨默?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵贩耐,是天一觀的道長。 經(jīng)常有香客問我厦取,道長潮太,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任虾攻,我火速辦了婚禮消别,結(jié)果婚禮上抛蚤,老公的妹妹穿的比我還像新娘。我一直安慰自己寻狂,他們只是感情好岁经,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛇券,像睡著了一般缀壤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纠亚,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天塘慕,我揣著相機與錄音,去河邊找鬼蒂胞。 笑死图呢,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骗随。 我是一名探鬼主播蛤织,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸿染!你這毒婦竟也來了指蚜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤涨椒,失蹤者是張志新(化名)和其女友劉穎摊鸡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚕冬,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡免猾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了囤热。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掸刊。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赢乓,靈堂內(nèi)的尸體忽然破棺而出忧侧,到底是詐尸還是另有隱情,我是刑警寧澤牌芋,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布蚓炬,位于F島的核電站,受9級特大地震影響躺屁,放射性物質(zhì)發(fā)生泄漏肯夏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望驯击。 院中可真熱鬧烁兰,春花似錦、人聲如沸徊都。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暇矫。三九已至主之,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間李根,已是汗流浹背槽奕。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留房轿,地道東北人粤攒。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像囱持,于是被迫代替她去往敵國和親夯接。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容