對APP單例的統(tǒng)一封裝(常規(guī)式)

單例模式(Singleton)是一種使用率非常高的設(shè)計模式朝抖,其主要目的在于保證某一類在運行期間僅被創(chuàng)建一個實例,并為該實例提供了一個全局訪問方法晌端,通常命名為getInstance()方法喜最。在APP開發(fā)中,我們會遇到大量的單例聚蝶,如各種ImageManager、ShareManager藻治、DownloadManger碘勉、ApiService等,此外單例的不恰當(dāng)使用還會帶來內(nèi)存泄露問題桩卵,因此验靡,對單例進行統(tǒng)一封裝倍宾、管理就顯得很有必要了。

先從單例模式說起

單例模式的實現(xiàn)主要有以下幾種方式:餓漢式胜嗓、懶漢式高职、靜態(tài)內(nèi)部類、enum等辞州,在實操過程中怔锌,我們經(jīng)常采用線程安全下的懶漢式和靜態(tài)內(nèi)部類式實現(xiàn),我們簡單回顧以下這兩種方式:

懶漢式

所謂懶漢变过,就是lazy load埃元,主要解決的問題是避免單例在classLoader的時候就被預(yù)先創(chuàng)建,而是在使用的時候再去創(chuàng)建媚狰,同時亚情,這這個模式下聽過線程鎖機制來保證線程安全,它的實現(xiàn)如下:

public class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

靜態(tài)內(nèi)部類式

這種方式是通過Java類加載機制來保證線程安全哈雏,JVM Class Loader時會執(zhí)行類的靜態(tài)變量賦初始值和執(zhí)行靜態(tài)代碼塊中的內(nèi)容,ClassLoader肯定是單線程的衫生,保證了單例的唯一性裳瘪。而靜態(tài)內(nèi)部類只有在調(diào)用了getIntance方法時,才會觸發(fā)內(nèi)部類的裝載罪针,因此這又保證了延遲加載彭羹。具體的實現(xiàn)如下:

public class Singleton {
    
    private static class Instance {
        private static Singleton instance = new Singleton();
    }

    private Singleton() {

    }

    public static Singleton getInstance() {
        return Instance.instance;
    }
}

再談單例的封裝

我們要解決的問題有兩個:

  1. 方便的單例創(chuàng)建;
  2. 避免內(nèi)存泄露泪酱,尤其是APP開發(fā)過程中的 context 造成的泄露問題

具體實現(xiàn)

DJContext

DJContext 持有 ApplicationContext, 避免因為 context 帶來的內(nèi)存泄露問題派殷;DJContext 提供統(tǒng)一的單例獲取方式,比如:
UserManager um = DJContext.getService(UserManager.class);

public final class DJContext {

    private static Context mContext;

    public static void init(Context context) {
        mContext = context.getApplicationContext();
    }

    public static <T> T getService(Class<T> tClass) {
        checkContextValid();
        return ServiceRegistry.getService(mContext, tClass);
    }

    private static void checkContextValid() {
        if (mContext == null) {
            throw new IllegalStateException("must call method [init] first !!!");
        }
    }
}

ServiceRegistry

采用靜態(tài)注冊的方式墓阀,注冊不同單例的獲取方式毡惜,同時通過內(nèi)部抽象類實現(xiàn)延遲加載。

final class ServiceRegistry {

    private static final Map<String, ServiceFetcher> SERVICE_FETCHERS = new HashMap<>();

    private static <T> void registerService(String name, ServiceFetcher<T> fetcher) {
        SERVICE_FETCHERS.put(name, fetcher);
    }

    static {
        registerService(UserManager.class.getName(), new ServiceFetcher<UserManager>() {
            @Override
            public UserManager createService(Context context) {
                return new UserManager();
            }
        });

        registerService(ImageManager.class.getName(), new ServiceFetcher<ImageManager>() {
            @Override
            public ImageManager createService(Context context) {
                return new ImageManager(context);
            }
        });
    }

    public static <T> T getService(Context context, Class<T> tClass) {
        final ServiceFetcher fetcher = SERVICE_FETCHERS.get(tClass.getName());
        return fetcher != null ? (T) fetcher.getService(context) : null;
    }

    private static abstract class ServiceFetcher<T> {
        private T mService;

        public final T getService(Context context) {
            synchronized (ServiceFetcher.this) {
                if (mService == null) {
                    mService = createService(context);
                }
                return mService;
            }
        }

        public abstract T createService(Context context);
    }
}

使用方式

使用分兩步:

1斯撮、DJContext的初始化:一般放在 Application.onCreate() 中经伙;

@Override
public void onCreate() {
    super.onCreate();
    DJContext.init(this);
}

2、通過DJContext獲取實例勿锅;

比如有個實例叫做:

public class ImageManager {

    private Context context;

    public ImageManager(Context context) {
        this.context = context;
    }
}

在ServiceRegistry預(yù)先進行register帕膜;
然后使用時通過以下方式:

ImageManager imgManager = DJContext.getService(ImageManager.class);

缺點
因為要實現(xiàn)單例可以被 register,所以單例類的構(gòu)造方式只能是 public/ protected 的溢十,這與單例的構(gòu)造方法需是 private 有所出入垮刹。針對這一點,使用過程中可以通過將所有的單例放到一個 package 下张弛,然后采用 protected 形式的構(gòu)造方法.

這里我把這種封裝稱之為常規(guī)式荒典,顯然還有一種非常規(guī)式酪劫,非常規(guī)式可以基于動態(tài)代理來實現(xiàn)封裝。敬請期待...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末种蝶,一起剝皮案震驚了整個濱河市契耿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌螃征,老刑警劉巖搪桂,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盯滚,居然都是意外死亡踢械,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門魄藕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來内列,“玉大人,你說我怎么就攤上這事背率』扒疲” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵寝姿,是天一觀的道長交排。 經(jīng)常有香客問我,道長饵筑,這世上最難降的妖魔是什么埃篓? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮根资,結(jié)果婚禮上架专,老公的妹妹穿的比我還像新娘。我一直安慰自己玄帕,他們只是感情好部脚,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桨仿,像睡著了一般睛低。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上服傍,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天钱雷,我揣著相機與錄音,去河邊找鬼吹零。 笑死罩抗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灿椅。 我是一名探鬼主播套蒂,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼钞支,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了操刀?” 一聲冷哼從身側(cè)響起烁挟,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎骨坑,沒想到半個月后撼嗓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡欢唾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年且警,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片礁遣。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡斑芜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出祟霍,到底是詐尸還是另有隱情杏头,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布沸呐,位于F島的核電站大州,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏垂谢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一疮茄、第九天 我趴在偏房一處隱蔽的房頂上張望滥朱。 院中可真熱鬧,春花似錦力试、人聲如沸徙邻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缰犁。三九已至,卻和暖如春怖糊,著一層夾襖步出監(jiān)牢的瞬間帅容,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工伍伤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留并徘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓扰魂,卻偏偏與公主長得像麦乞,于是被迫代替她去往敵國和親蕴茴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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