单例模式

单例模式是应用非常广泛,使用非常简单的一种设计模式,单例模式要求单例类的对象必须保证只有一个实例的存在,例如在 Android 中一般情况下只有一个 Application 实例。然而虽然单例模式理解起来非常简单,但是要写出一个万无一失的单例模式还是不容易的,因此这篇博客将首先介绍单例模式的各种写法,接下来会介绍 Android 源码中的单例模式的应用。

单例模式的使用场景

单例模式的使用场景是某些类的创建需要消耗很多资源,如 ImageLoader , ImageLoader 的构建需要缓存和线程池的构造,或者某些对象在逻辑上应该只有一个实例的存在,例如 Android 中的 Application 对象。在这些情况下我们就应该考虑单例模式。

单例模式的写法

单例模式有如下几个特点:

  1. 构造函数为 private 的
  2. 通过一个静态方法返回单例对象
  3. 确保单例对象的实例只有一个
  4. 确保单例对象在反序列化时不会重新构造对象,这个特点在一般情况下不会做要求,但是在一些特殊情况下应该保证这一点

单例模式实现——饿汉模式

1
2
3
4
5
6
7
8
9
10
public class SingleInstance {
private static SingleInstance sSingleInstance = new SingleInstance();

private SingleInstance() {
}

public SingleInstance getInstance() {
return sSingleInstance;
}
}

优点:实现简单,这可能是最简单的单例模式的写法了,获取单例对象性能较高
缺点:系统一初始化就要构造单例对象,使得初始化占用较多时间

单例模式实现——懒汉模式

1
2
3
4
5
6
7
8
9
10
public class SingleInstance {
private static SingleInstance sSingleInstance;

private SingleInstance() {
}

public static synchronized SingleInstance getInstance() {
return sSingleInstance;
}
}

优点:需要使用时才加载资源,节约资源
缺点:为了线程同步,在 getInstance 前面加了 synchronized 关键字,由于每次获取单例对象时都需要进行线程同步,影响性能,因此一般情况下不建议使用懒汉模式。

单例模式实现——双检查锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SingleInstance {
private static SingleInstance sSingleInstance;

private SingleInstance() {
}

public static SingleInstance getInstance() {
if (sSingleInstance == null) {
synchronized (SingleInstance.class) {
if (sSingleInstance == null) {
sSingleInstance = new SingleInstance();
}
}
}
return sSingleInstance;
}
}

双重检查锁乍看上去解决了上面两种写法的问题,但是这种写法也不是万无一失的。下面我们分析一下存在的问题:
sSingleInstance = new SingleInstance();这句代码在 JVM 中执行时会分为三步:(1)为 SingleInstance 分配内存(2)调用 SingleInstance 的构造函数,初始化成员变量(3)为 sSingleInstance 赋值,正常情况下当然不会有问题,但是 JVM 会进行代码优化,(2)和(3)的执行先后不确定,如果 SingleInstance 的构造函数中初始化了成员变量,那么就会有问题,有可能某次执行时拿到的单例对象的成员变量没有经过初始化,这个问题不易发现,我们在开发中要注意,为了解决这个问题可以将 sSingleInstance 用 volatile 进行修饰。对 volatile 不熟悉的同学可自行查阅相关资料了解。需要注意的是这三种写法都没有关注到对象被反序列化的情况。

单例模式实现——静态内部类单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SingleInstance {

private SingleInstance() {
}

public static SingleInstance getInstance() {
return SingleTanceHolder.sSingleInstance;
}

private static class SingleTanceHolder {
private static SingleInstance sSingleInstance = new SingleInstance();
}
}

这种写法,第一次加载 SingleInstance 时并不会初始化 sSingleInstance,只有第一次调用 getInstance 时才会初始化 getInstance,这种方法不但保证了单例的唯一性,确保了线程安全,且延迟了单例的实例化,因此推荐使用这种写法。然而这种写法也没有考虑到被反序列化的情况。

为了解决被反序列化时会重新生成对象的问题,我们给出两种解决办法:
如果使用上面几种写法,则需要加入如下方法:

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

这个方法可以用来控制对象的反序列化。
我们也可以使用枚举解决这个问题,枚举在默认情况下就是一个单例,且线程安全,而且和普通类一样可以具有属性和方法,并且在反序列化时不会重新构造对象。
1
2
3
public enum SingleInstance {
INSTANCE;
}

Android 源码中的单例模式

Android 源码中的设计模式使用可谓是顺手拈来啊,我们来看看 Android 源码中哪里使用了单例模式。

  1. Application : 一个应用一般情况下只有一个进程,也就只有一个 Application 实例, Application 的单例并不是我们上面介绍的几种方式,而是由 LoadedApk 类的 makeApplication 方法控制的,在 ActivityThread 的 performLaunchActivity 方法里会调用 makeApplication 来构造 Application 实例,下面是 makeApplication 的源码。可以看到 Application 的实例是利用 Instrumentation 的 newApplication 方法,利用反射进行构造的,然后会构造 ContextImpl ,然后将 Application 和 ContextImpl 绑定,最后调用了 Application 的 onCreate 方法,至此 Application 也就构造完成了。
    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
    40
    41
    42
    43
    44
    45
    46
    47
    48
    public Application makeApplication(boolean forceDefaultAppClass,
    Instrumentation instrumentation)
    {

    if (mApplication != null) {
    return mApplication;
    }

    Application app = null;

    String appClass = mApplicationInfo.className;
    if (forceDefaultAppClass || (appClass == null)) {
    appClass = "android.app.Application";
    }

    try {
    java.lang.ClassLoader cl = getClassLoader();
    if (!mPackageName.equals("android")) {
    initializeJavaContextClassLoader();
    }
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    app = mActivityThread.mInstrumentation.newApplication(
    cl, appClass, appContext);
    appContext.setOuterContext(app);
    } catch (Exception e) {
    if (!mActivityThread.mInstrumentation.onException(app, e)) {
    throw new RuntimeException(
    "Unable to instantiate application " + appClass
    + ": " + e.toString(), e);
    }
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
    try {
    instrumentation.callApplicationOnCreate(app);
    } catch (Exception e) {
    if (!instrumentation.onException(app, e)) {
    throw new RuntimeException(
    "Unable to create application " + app.getClass().getName()
    + ": " + e.toString(), e);
    }
    }
    }

    //........

    return app;
    }
  2. 系统级的服务,如: WindowsManagerService 、 ActivityManagerService 等。我们知道获取这些服务都会调用 Context 的 getSystemService 方法进行获取,我们知道 Context 的最后实现类是 ContextImpl ,因此我们直接跟进 ContextImpl 的 getSystemService 方法。可以看到 在调用 Context 的 getSystemService 方法时会去SYSTEM_SERVICE_MAP 中查找 ServiceFetcher ,最终会通过 ServiceFetcher 的 getService 方法获取对应的 service ,而这些 ServiceFetcher 是在 ContextImpl 的静态块中通过 registerService 方法进行注册的,因为是在静态块中进行注册的,所以保证了它们的单例,这些系统级的服务以单例的形式存在减少了资源消耗。
    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
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    @Override
    private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = new HashMap<String, ServiceFetcher>();

    public Object getSystemService(String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
    return fetcher == null ? null : fetcher.getService(this);
    }

    private static void registerService(String serviceName, ServiceFetcher fetcher) {
    if (!(fetcher instanceof StaticServiceFetcher)) {
    fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
    }
    SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
    }

    static {
    registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {
    public Object getService(ContextImpl ctx) {
    return AccessibilityManager.getInstance(ctx);
    }});

    registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
    public Object createService(ContextImpl ctx) {
    return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
    }});

    registerService(ALARM_SERVICE, new ServiceFetcher() {
    public Object createService(ContextImpl ctx) {
    IBinder b = ServiceManager.getService(ALARM_SERVICE);
    IAlarmManager service = IAlarmManager.Stub.asInterface(b);
    return new AlarmManager(service, ctx);
    }});

    registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
    public Object createService(ContextImpl ctx) {
    return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
    }});

    registerService(LOCATION_SERVICE, new ServiceFetcher() {
    public Object createService(ContextImpl ctx) {
    IBinder b = ServiceManager.getService(LOCATION_SERVICE);
    return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
    }});

    registerService(NETWORK_POLICY_SERVICE, new ServiceFetcher() {
    @Override
    public Object createService(ContextImpl ctx) {
    return new NetworkPolicyManager(INetworkPolicyManager.Stub.asInterface(
    ServiceManager.getService(NETWORK_POLICY_SERVICE)));
    }
    });

    registerService(NOTIFICATION_SERVICE, new ServiceFetcher() {
    public Object createService(ContextImpl ctx) {
    final Context outerContext = ctx.getOuterContext();
    return new NotificationManager(
    new ContextThemeWrapper(outerContext,
    Resources.selectSystemTheme(0,
    outerContext.getApplicationInfo().targetSdkVersion,
    com.android.internal.R.style.Theme_Dialog,
    com.android.internal.R.style.Theme_Holo_Dialog,
    com.android.internal.R.style.Theme_DeviceDefault_Dialog,
    com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),
    ctx.mMainThread.getHandler());
    }});

    registerService(WINDOW_SERVICE, new ServiceFetcher() {
    Display mDefaultDisplay;
    public Object getService(ContextImpl ctx) {
    Display display = ctx.mDisplay;
    if (display == null) {
    if (mDefaultDisplay == null) {
    DisplayManager dm = (DisplayManager)ctx.getOuterContext().
    getSystemService(Context.DISPLAY_SERVICE);
    mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
    }
    display = mDefaultDisplay;
    }
    return new WindowManagerImpl(display);
    }});
    //...

    }