设计模式之单例模式及在Android源码中的应用

概述

主要总结单例模式的特点和几种使用方式,以及在Android源码中单例模式的使用,由于单例模式在android中的广泛使用,所以主要在剖析Android中getSystemService流程(即获取系统服务的流程)中讲述单例模式的使用。
单例模式:

  • 单例模式在内存中只有一个实例,减少了内存开销
  • 单例模式可以避免对资源的多重占用,例如一个写文件时,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作
  • 单例模式可以在系统设置全局的访问点,优化和共享资源访问
  • 使用单例模式时,考虑较多的就是多线程的情况下如何防止被多线程同时创建等问题

关键点:

  1. 构造函数不对外开放,一般为private
  2. 通过静态方法或者枚举返回单例对象
  3. 确保单例类的对象有且只有一个,尤其在多线程的环境下
  4. 确保单例类对象在反序列化时不会重新构建对象

单例模式的使用方式

饿汉模式

1
2
3
4
5
6
7
8
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}

这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。这种方式基于类加载机制避免了多线程的同步问题

懒汉模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}

缺点:效率有点低,每次调用实例都要判断同步锁

双重检验锁(Double CheckLock)

1
2
3
4
5
6
7
8
9
10
11
/*双重锁定:只在第一次初始化的时候加上同步锁*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}

优点:既能够在需要时初始化单例,又能够保证线程安全,且在初始化后调用getInstance不进行同步锁。
缺点:不同平台编译过程中可能会存在严重安全隐患。
在执行到instance = newSingleton()语句时,最终会编译成多条汇编指令,大致做了3件事:

  1. 给Singleton实例分配内存
  2. 调用Singleton()的构造函数,初始化成员字段
  3. 将instance对象指向分配的内存空间(此时instance就不是null)

在JVM编译的过程中会出现指令重排的优化过程既可能执行顺序为1-3-2,这就会导致当 instance实际上还没初始化,就可能被分配了内存空间,也就是说会出现 instance !=null 但是又没初始化的情况,这样就会导致当切换到其他线程时返回的instance不完整

解决方案:只需将instance定义为 private volatile static Singleton instance = null; 这样就保证instance对象每次都是从主存中读取,就可以使用DCL的写法来完成单例模式。当然,volatile或多或少也会影响到性能。

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SingletonInner {
private SingletonInner() {
}
public static SingletonInner getInstance() {
return SingletonHolder.instance;
}
protected void method() {
System.out.println("SingletonInner");
}
/**
* 内部类实现单例模式
* 延迟加载,减少内存开销
*/
private static class SingletonHolder {
private static SingletonInner instance = new SingletonInner();
}
}

调用:

1
SingletonInner.getInstance().method();

优点:延迟加载,线程安全(java中class加载时互斥的),也减少了内存消耗

枚举单例

上面的几种情况,在反序列化时会出现重新创建对象的情况。通过序列化将一个单例的实例写入到磁盘,然后再读回来,但反序列化时会通过一个特别的钩子函数readResolve()去创建类的一个新的实例,但开发人员可以通过重写该方法杜绝反序列化时重新生成对象,如下所示:

1
2
3
private Object readResolve() throws ObjectStreamException{
return instance;
}

在readResolve方法中instance对象返回,而不是重新创建一个新的对象。但对于枚举,即使反序列化时也不会出现生成新的实例。
枚举单例的使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public enum SingletonEnum {
/**
* 1.从Java1.5开始支持;
* 2.无偿提供序列化机制;
* 3.绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候;
*/
instance;
SingletonEnum() {
}
public void method() {
System.out.println("SingletonEnum");
}
}

调用:

1
SingletonEnum.instance.method();

使用容器实现单例

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SingletonManager {
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() {
  }
  public static void registerService(String key, Objectinstance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;
  }
}

用SingletonManager 将多种的单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

Android中getSystemService流程

在平时的Android开发中,我们经常会通过Context来获取系统服务,比如ActivityManagerService,AccountManagerService等系统服务,今天我们就来看下getSystemService(String name)的整个调用流程。

Context和ContextImpl

打开Context类,可以看到Context是一个抽象类,那么getSystemService一定是在其实现类来调用的,具体我们都猜得到ContextImpl

1
2
3
public abstract class Context {
......
}

当我们启动一个Activity的时候,其实是在ActivityThread#performLaunchActivity方法中启动的,一个Activity的入口是ActivityThread的main方法,看下main方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
.......
// 主线程消息循环
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到上面在main方法中,调用了自身的attach方法

1
2
3
4
5
6
7
8
9
10
11
12
13
private void attach(boolean system) {
........
if (!system) {
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
// 通过binder获取到的ActivityManagerService关联当前mAppThread
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
}

在main方法中,通过调用activityThread的attach方法,并且参数为false,表示非系统应用,会通过binder与ActivityManagerService通信,并且最后会调用performLaunchActivity方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
....
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
// 创建Context的实例
Context appContext = createBaseContextForActivity(r, activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
.......
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
.....
}
return activity;
}

可以看到创建Context的具体实现是在ActivityThread#createBaseContextForActivity方法中完成的

1
2
3
4
5
6
7
8
9
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
// 可以看到这里创建的具体实例是ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
......
return baseContext;
}

可以看到Context的实现类就是ContextImpl,那么关于getSystemService的具体实现也应该在ContextImpl里面了。

getSystemService的具体实现

ContextImpl#getSystemService的实现如下

1
2
3
4
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}

可以看到实际上ContextImpl也是通过SystemServiceRegistry.getSystemService来获取具体的服务,SystemServiceRegistry是个final类型的类。那么下面看看SystemServiceRegistry类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
final class SystemServiceRegistry {
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES = new HashMap<Class<?>, String>();
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<?>>();
private SystemServiceRegistry() { }
static {
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
.......
}
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
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);
}
......
}

可看到实际上获取系统服务是通过ServiceFetcher的getService来获取的,并且SYSTEM_SERVICE_FETCHERS实际上就是一个HashMap实例,通过put方法为它赋值的。registerService方法是用来注册系统服务的,当注册系统服务的时候,就会调用到SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);将当前的服务put到SYSTEM_SERVICE_NAMES集合中

1
2
3
static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}

ServiceFetcher是一个接口,又三个实现类:CachedServiceFetcher,StaticServiceFetcher,StaticApplicationContextServiceFetcher,具体不同的服务可能对应不同的实现类。看一下CachedServiceFetcher的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
synchronized (cache) {
// Fetch or create the service.
Object service = cache[mCacheIndex];
if (service == null) {
service = createService(ctx);
cache[mCacheIndex] = service;
}
return (T)service;
}
}
public abstract T createService(ContextImpl ctx);
}

这些系统服务都是在SystemServiceRegistry的static静态代码块中进行注册的,即上面代码中的static代码块。

这里,我们以ActivityManagerService为例来进行学习。在注册该服务的时候,将Context.ACTIVITY_SERVICE作为键,将CachedServiceFetcher作为值,并且重写了createService方法,createService是在当前ServiceFetcher的getService里执行的,这里会返回一个ActivityManager的实例,ctx.mMainThread.getHandler()就是ActivityThread中的H,启动activity最终就会通过ActivityThread$H的handleMessage进行处理,实际上,包括service的启动以及Broadcast的处理都是在H里处理的。

单例模式的应用:
这些服务以键值对的形式存储在一个HashMap(即SYSTEM_SERVICE_FETCHERS)中,用户使用时只需要根据key值获取对应的ServiceFetcher,然后通过ServiceFetcher对象的getService函数来获取具体的服务对象。当第一次获取时,会调用ServiceFetcher的createService函数创建服务对象,然后缓存在一个列表HashMap中,下次再取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果,这里使用单例的方式是使用容器的单例形式。