Android設計模式之單例模式
單例模式的定義:
確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項。
實現單例模式的關鍵點:
1.建構函式不對外開放,一般為private。
2.通過一個靜態方法或者列舉返回單例類物件。
3.確保單例類的物件有且只有一個,尤其是在多執行緒環境下。
4.確保單例類物件在反序列化時不會重新構建物件。
1.餓漢式 單例模式 (在宣告的時候已經初始化了)
public class Singleton { private static final Singleton s = new Singleton(); private Singleton () { } public static Singleton getIntance() { return s; } }
2.懶漢式 單例模式
public class SingletonTwo { private static SingletonTwo singletonTwo; private SingletonTwo () { } public static synchronized SingletonTwo getInstance() { if (singletonTwo == null) { singletonTwo = new SingletonTwo(); } return singletonTwo; } }
上面通過新增 synchronized關鍵字,使得getInstance()是一個同步方法,保證多執行緒情況下單例物件的唯一性。
懶漢式優點:
1.單例只有在使用時才會被例項化,在一定程度上節約了資源。
缺點:
1.第一次載入時需要及時進行例項化,反應稍慢。
2.每次呼叫getIntance都進行同步,造成不必要的同步開銷。
3.Double Check Lock(DCL) 實現單例
public class DoubleSingleInstance { private static DoubleSingleInstance sSingleInstance; private DoubleSingleInstance() { } public static DoubleSingleInstance getIntance() { if (sSingleInstance == null) { synchronized (DoubleSingleInstance.class) { if (sSingleInstance == null) { sSingleInstance = new DoubleSingleInstance(); } } } return sSingleInstance; } }
在getInstance()中對instance進行兩次判空,第一層判空:避免不必要的同步。第二次判空:在instance為null的情況下建立例項。
/*****/
假設執行緒A執行到sInstance = new Singletion()語句,這裡看起來是一句程式碼,但實際上不是一個原子操作。
最終
被編譯成多條彙編指令:
1.給Singletom的例項分配記憶體;
2.呼叫Singleton()的建構函式,初始化成員欄位;
3.將sInstance物件指向分配的記憶體空間(此時sInstance就不是null了)。
由於java編譯器允許處理器亂序執行。(可能導致單例物件的某些欄位沒有初始化)
3.1在JDK1.5之後,SUN發現該問題,調整了JVM,具體化了volatile關鍵字。
所以在JDK1.5以及其之後,只需要將sInstance定義成private volatile static Singleton sInstance = null,就可以保證sInstance物件每次都是從主記憶體中讀取。
4.靜態內部類單例模式
DCL雖然在一定程度上解決了資源消耗、多餘同步、執行緒安全等問題。
(但還是會存在 雙重檢查鎖定(DCL)失效)
public class InnerSingleton {
private InnerSingleton() {
}
public static InnerSingleton getInstance() {
return SingletonHolder.sIntance;
}
/**
* 靜態內部類
*/
private static class SingletonHolder{
private static final InnerSingleton sIntance = new InnerSingleton();
}
}
使用靜態內部類單例模式的優點:
第一次載入Singleton類時並不會初始化sIntance,只有第一次呼叫Singleton的getInstance方法才會導致sInstance被初始化。
第一次呼叫getInstance()方法會導致虛擬機器載入SingletonHolder類。
優點:
保證執行緒安全。
保證單例物件的唯一性。
延遲單例的例項化。
5.列舉單例
public enum SingleEnum {
INSTANCE;
}
最重要的是預設列舉例項的建立是執行緒安全的,並且在任何情況下它都是一個單例。
在1~4種建立單例模式的方法中,在反序列化的情況下會重新建立物件。
我們知道通過序列化可以將一個單例的例項物件寫到磁碟,然後再讀回來,從而有效獲取一個例項。
即使建構函式是私有的,反序列化依然可以通過特殊的途徑去建立類的一個新的例項,相當於呼叫該類的建構函式。
反序列化操作提供了一個很特別的鉤子函式,類中具有一個私有的readResolve()函式,這個函式可以讓開發人員控制物件的反序列化。
public class SingletonSerializable implements Serializable{
private static final long serialVersionUID = 0L;
private static final SingletonSerializable INSTANCE = new SingletonSerializable();
private SingletonSerializable() {
}
public static SingletonSerializable getInstance() {
return INSTANCE;
}
private Object readResolve() throws ObjectStreamException{
return INSTANCE;
}
}
也就是在readResolve方法中將單例物件返回,而不是重新生成一個新物件。
對於列舉,並不存在這個問題,因為即使反序列化它也不會重新生成新的例項。
(1)可序列化類中的欄位型別不是java的內建型別,那麼該欄位型別也需要實現Serializable介面。
(2)如果你調整了可序列化類的內部結構,例如新增、去除某個欄位,但是沒有修改serialVersionUID,那麼會引發java.io.InvalidClassException 或者導致某個屬性為0或者null,此時最好的方案是我們直接將serialVersionUID設定為0L,這樣即使修改了內部結構,我們反序列化也不會丟擲java.io.InvalidClassException ,只是那些新增的欄位會為0或者null。
6.使用容器實現單例模式
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String, Object>();
private SingletonManager () {
}
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
在程式的初始,將多種單例型別注入到一個統一的管理類中,在使用時根據key獲取對應型別的物件。
單例模式核心:
1.將建構函式私有化。
2.通過靜態方法獲取唯一的例項。
3.保證執行緒安全
4.防止反序列化導致重新生成例項物件等問題
//Android framework中大多使用靜態內部類的方式實現單例。
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
package android.util;
/**
* Singleton helper class for lazily initialization.
*
用於延遲初始化的Singleton helper類。
* Modeled after frameworks/base/include/utils/Singleton.h
*
* @hide
*/
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
Android原始碼中的單例模式:
在android系統中,通過Context獲取系統級別的服務,如WindowManagerService,ActivityManagerService等,常用是LayoutInflater,這些服務會在合適的時候以單例的形式註冊在系統中, 這些服務會在合適的時候以單例的形式註冊在系統中,在我們需要的時候通過Context的getSystemService(String name)獲取。
#LayoutInflater
/**
* Obtains the LayoutInflater from the given context.
*/
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
#ContextImpl
public class ContextImpl extends Context {
... ...
... ...
}
#ContextImpl
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
#ContextImpl
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
/** *管理所有系統服務 * Manages all of the system services that can be returned by {@link Context#getSystemService}. * Used by {@link ContextImpl}. * @hide */ public final class SystemServiceRegistry { ... ... }
/**
* Gets the name of the system-level service that is represented by the specified class.
*/
public static String getSystemServiceName(Class<?> serviceClass) {
return SYSTEM_SERVICE_NAMES.get(serviceClass);
}
// Service registry information.
//靜態初始化完成後,此資訊永遠不會更改
// This information is never changed once static initialization has completed.
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
new HashMap<Class<?>, String>();
//Service容器
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
private static int sServiceCacheSize;
/**
* 從給定的上下文獲取系統服務。
* Gets a system service from a given context.
*/
//根據key獲取對應的服務
public static Object getSystemService(ContextImpl ctx, String name) {
//根據name獲取對應的服務
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
/**
*提取服務的類的基本介面。這些物件只能在靜態初始化期間建立。
* Base interface for classes that fetch services.
* These objects must only be created during static initialization.
* (竟然還可以是abstract 的介面)
*/
public static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}
/**
* Override this class when the system service constructor needs a
* ContextImpl and should be cached and retained by that context.
* 當系統服務建構函式需要ContextImpl時覆蓋此類,並且應該由該上下文進行快取和保留。
*/
public static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
private final int mCacheIndex;
public CachedServiceFetcher() {
mCacheIndex = sServiceCacheSize++;
}
// 獲取系統服務
@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;//獲取Service快取
synchronized (cache) {
// Fetch or create the service.
Object service = cache[mCacheIndex];
if (service == null) {
try {
service = createService(ctx);
cache[mCacheIndex] = service;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return (T)service;
}
}
//子類覆寫該方法以建立服務物件
public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
/**
* Override this class when the system service does not need a ContextImpl
* and should be cached and retained process-wide.
*/
static abstract class StaticServiceFetcher<T> implements ServiceFetcher<T> {
private T mCachedInstance;
@Override
public final T getService(ContextImpl ctx) {
synchronized (StaticServiceFetcher.this) {
if (mCachedInstance == null) {
try {
mCachedInstance = createService();
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return mCachedInstance;
}
}
public abstract T createService() throws ServiceNotFoundException;
}
/**
* Like StaticServiceFetcher, creates only one instance of the service per application, but when
* creating the service for the first time, passes it the application context of the creating
* application.
*
* TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
* case where multiple application components each have their own ConnectivityManager object.
*/
static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
private T mCachedInstance;
@Override
public final T getService(ContextImpl ctx) {
synchronized (StaticApplicationContextServiceFetcher.this) {
if (mCachedInstance == null) {
Context appContext = ctx.getApplicationContext();
// If the application context is null, we're either in the system process or
// it's the application context very early in app initialization. In both these
// cases, the passed-in ContextImpl will not be freed, so it's safe to pass it
// to the service. http://b/27532714 .
try {
mCachedInstance = createService(appContext != null ? appContext : ctx);
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return mCachedInstance;
}
}
public abstract T createService(Context applicationContext) throws ServiceNotFoundException;
}
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
* 用上下文靜態註冊系統服務。
* 該方法只能在靜態初始化期間呼叫。
*/
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
//靜態程式碼塊,第一次載入該類的時候執行(只執行一次,保證例項的唯一性)
static {
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
@Override
public AccessibilityManager createService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
new CachedServiceFetcher<CaptioningManager>() {
@Override
public CaptioningManager createService(ContextImpl ctx) {
return new CaptioningManager(ctx);
}});
registerService(Context.ACCOUNT_SERVICE, AccountManager.class,
new CachedServiceFetcher<AccountManager>() {
@Override
public AccountManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.ACCOUNT_SERVICE);
IAccountManager service = IAccountManager.Stub.asInterface(b);
return new AccountManager(ctx, service);
}});
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
... ...
}
從ContextImpl類的部分程式碼可以看到,在虛擬機器第一次載入該類的時候會註冊各種ServiceFatcher,其中就包含了LayoutInflater Service。
將這些服務以鍵值對的形式儲存在一個HashMap中,使用者使用時只需要根據key來獲取對應的ServiceFetcher,然後通過ServiceFetcher物件的getService方法來獲取具體的服務物件。
當第一次獲取是,會呼叫ServiceFetcher的createService方法建立服務物件,然後將該物件快取到一個列表中,下次再取時直接從快取中獲取,避免重複建立物件,從而達到單例的效果。
通過HashMap容器的單例模式實現方式,系統核心服務以單例形式存在,減少了資源消耗。
參考書籍:<<Android原始碼設計模式>>