单例模式是应用非常广泛,使用非常简单的一种设计模式,单例模式要求单例类的对象必须保证只有一个实例的存在,例如在 Android 中一般情况下只有一个 Application 实例。然而虽然单例模式理解起来非常简单,但是要写出一个万无一失的单例模式还是不容易的,因此这篇博客将首先介绍单例模式的各种写法,接下来会介绍 Android 源码中的单例模式的应用。
单例模式的使用场景
单例模式的使用场景是某些类的创建需要消耗很多资源,如 ImageLoader , ImageLoader 的构建需要缓存和线程池的构造,或者某些对象在逻辑上应该只有一个实例的存在,例如 Android 中的 Application 对象。在这些情况下我们就应该考虑单例模式。
单例模式的写法
单例模式有如下几个特点:
- 构造函数为 private 的
- 通过一个静态方法返回单例对象
- 确保单例对象的实例只有一个
- 确保单例对象在反序列化时不会重新构造对象,这个特点在一般情况下不会做要求,但是在一些特殊情况下应该保证这一点
单例模式实现——饿汉模式
1 | public class SingleInstance { |
优点:实现简单,这可能是最简单的单例模式的写法了,获取单例对象性能较高
缺点:系统一初始化就要构造单例对象,使得初始化占用较多时间
单例模式实现——懒汉模式
1 | public class SingleInstance { |
优点:需要使用时才加载资源,节约资源
缺点:为了线程同步,在 getInstance 前面加了 synchronized 关键字,由于每次获取单例对象时都需要进行线程同步,影响性能,因此一般情况下不建议使用懒汉模式。
单例模式实现——双检查锁
1 | public class SingleInstance { |
双重检查锁乍看上去解决了上面两种写法的问题,但是这种写法也不是万无一失的。下面我们分析一下存在的问题:
sSingleInstance = new SingleInstance();这句代码在 JVM 中执行时会分为三步:(1)为 SingleInstance 分配内存(2)调用 SingleInstance 的构造函数,初始化成员变量(3)为 sSingleInstance 赋值,正常情况下当然不会有问题,但是 JVM 会进行代码优化,(2)和(3)的执行先后不确定,如果 SingleInstance 的构造函数中初始化了成员变量,那么就会有问题,有可能某次执行时拿到的单例对象的成员变量没有经过初始化,这个问题不易发现,我们在开发中要注意,为了解决这个问题可以将 sSingleInstance 用 volatile 进行修饰。对 volatile 不熟悉的同学可自行查阅相关资料了解。需要注意的是这三种写法都没有关注到对象被反序列化的情况。
单例模式实现——静态内部类单例模式
1 | public class SingleInstance { |
这种写法,第一次加载 SingleInstance 时并不会初始化 sSingleInstance,只有第一次调用 getInstance 时才会初始化 getInstance,这种方法不但保证了单例的唯一性,确保了线程安全,且延迟了单例的实例化,因此推荐使用这种写法。然而这种写法也没有考虑到被反序列化的情况。
为了解决被反序列化时会重新生成对象的问题,我们给出两种解决办法:
如果使用上面几种写法,则需要加入如下方法:1
2
3private Object readResolve() throws ObjectStreamException {
return sSingleInstance;
}
这个方法可以用来控制对象的反序列化。
我们也可以使用枚举解决这个问题,枚举在默认情况下就是一个单例,且线程安全,而且和普通类一样可以具有属性和方法,并且在反序列化时不会重新构造对象。1
2
3public enum SingleInstance {
INSTANCE;
}
Android 源码中的单例模式
Android 源码中的设计模式使用可谓是顺手拈来啊,我们来看看 Android 源码中哪里使用了单例模式。
- 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
48public 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;
} - 系统级的服务,如: 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
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() {
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);
}});
//...
}