對APP單例的統一封裝(常規式)

單例模式(Singleton)是一種使用率很是高的設計模式,其主要目的在於保證某一類在運行期間僅被建立一個實例,併爲該實例提供了一個全局訪問方法,一般命名爲getInstance()方法。在APP開發中,咱們會遇到大量的單例,如各類ImageManager、ShareManager、DownloadManger、ApiService等,此外單例的不恰當使用還會帶來內存泄露問題,所以,對單例進行統一封裝、管理就顯得頗有必要了。設計模式

先從單例模式提及

單例模式的實現主要有如下幾種方式:餓漢式、懶漢式、靜態內部類、enum等,在實操過程當中,咱們常常採用線程安全下的懶漢式和靜態內部類式實現,咱們簡單回顧如下這兩種方式:安全

懶漢式

所謂懶漢,就是lazy load,主要解決的問題是避免單例在classLoader的時候就被預先建立,而是在使用的時候再去建立,同時,這這個模式下聽過線程鎖機制來保證線程安全,它的實現以下:bash

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;
    }

}
複製代碼

靜態內部類式

這種方式是經過Java類加載機制來保證線程安全,JVM Class Loader時會執行類的靜態變量賦初始值和執行靜態代碼塊中的內容,ClassLoader確定是單線程的,保證了單例的惟一性。而靜態內部類只有在調用了getIntance方法時,纔會觸發內部類的裝載,所以這又保證了延遲加載。具體的實現以下:ide

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

	private Singleton() {

	}

	public static Singleton getInstance() {
        return Instance.instance;
    }
}
複製代碼

再談單例的封裝

咱們要解決的問題有兩個:fetch

  1. 方便的單例建立;
  2. 避免內存泄露,尤爲是APP開發過程當中的 context 形成的泄露問題

具體實現

DJContextui

DJContext 持有 ApplicationContext, 避免由於 context 帶來的內存泄露問題;DJContext 提供統一的單例獲取方式,好比: UserManager um = DJContext.getService(UserManager.class);this

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 !!!");
        }
    }
}
複製代碼

ServiceRegistryspa

採用靜態註冊的方式,註冊不一樣單例的獲取方式,同時經過內部抽象類實現延遲加載。線程

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);
    }
}
複製代碼

使用方式

使用分兩步:設計

一、DJContext的初始化:通常放在 Application.onCreate() 中;

@Override
public void onCreate() {
    super.onCreate();
    DJContext.init(this);
}
複製代碼

二、經過DJContext獲取實例;

好比有個實例叫作:

public class ImageManager {

    private Context context;

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

在ServiceRegistry預先進行register;
而後使用時經過如下方式:

ImageManager imgManager = DJContext.getService(ImageManager.class);
複製代碼

缺點 由於要實現單例能夠被 register,因此單例類的構造方式只能是 public/ protected 的,這與單例的構造方法需是 private 有所出入。針對這一點,使用過程當中能夠經過將全部的單例放到一個 package 下,而後採用 protected 形式的構造方法.

這裏我把這種封裝稱之爲常規式,顯然還有一種很是規式,很是規式能夠基於動態代理來實現封裝。敬請期待...

相關文章
相關標籤/搜索