1. 程式人生 > >Android設計模式之單例模式

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原始碼設計模式>>