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

每一个ContextImpl对象都会初始化一个mServiceCache数组,这个数组的大小就是系统服务的数量。

 #ContextImpl
// The system service cache for the system services that are cached per-ContextImpl.
 final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();

 

通过Context对象获取系统服务对象。

/**
 *管理所有系统服务 ,SystemServiceRegistry 起到一个工具类的作用,其构造方法私有。
 * Manages all of the system services that can be returned by {@link Context#getSystemService}.
 * Used by {@link ContextImpl}.
 * @hide
 */
public final class SystemServiceRegistry {
    ... ...
}

SystemServiceRegistry首次加载。

以LayoutInflater注册为例:registerService方法注册LayoutInflater时,需要参数Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,以及CachedServiceFetcher对象。

     registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
 

 

/**

* 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源码设计模式>>

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值