Android源码中的设计模式之单例模式

       Android源码中包含许多配置类和管理者类,为了保证他们存储的数据一致性。通常他们被包装为单例模式。这一节我们就来介绍介绍Android源码中有些类用到单例模式,并摘出几个常见的类,稍作分析,帮助大家认识什么是单例模式以及他能带来的好处。

定义


单例模式:确保一个类只有一个实例,并提供一个全局访问点。单例模式的结构比较简单,主要包含三点:

  • 私有构造器,因为单例模式只允许类只有一个实例,所以通过修饰符限制外界通过new创建对象。
  • 私有静态实例,static修饰类的变量时,该变量只有一份存储空间,无论创建多少个类对象,他们都共用这一份存储空间。static关键字在单例模式中可以很好的保证返给调用者的数据都是一样的,确保类实例唯一性。
  • 静态方法,静态方法只和类有关,而不是类对象。通过{类.静态方法}就可以完成调用拿到第二步中保存的数据。方法名称一般是getInstance()。

UML类图


  • 类图以LocalBroadcastManager为例

源码中的单例模式

首先我们看第一个例子:

LocalBroadcastManager

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

public class LocalBroadcastManager {
private static final Object mLock = new Object();

// 第二点:私有静态实例
private static LocalBroadcastManager mInstance;

// 第一点:私有构造器
private LocalBroadcastManager(Context context) {

...

}

// 第三点:静态方法
public static LocalBroadcastManager getInstance(Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}

...

       LocalBroadcastManager用于发送应用内部广播数据,发送的数据不会暴露给其他应用,提供更好的安全性,并且运行效率也很高。

       在上面的源码中我按照定义中提到的三点做了标注,这样更容易阅读和理解。第三点静态方法里面用到线程同步处理,这么做的原因是:在多线程中处理单例模式,有可能出现两个线程同时访问类A,此时恰恰类A中静态实例mInstance为空,那么这两个线程会同时调用getInstance()创建mInstance,这就导致单例模式“失灵”了,出现两个实例。为了避免此类问题发生,所以加入线程锁,也就是源码中synchronized (mLock) {}这部分代码。关于线程锁的更多知识,请大家去网上搜索资料,这里就不做介绍了。

第二个例子是:

InputMethodManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public final class InputMethodManager {

// 第二点:私有静态变量
private static InputMethodManager sInstance;

// 第一点:私有构造器
private InputManager(IInputManager im) {
mIm = im;
}

// 第三点:静态方法
public static InputManager getInstance() {
synchronized (InputManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
sInstance = new InputManager(IInputManager.Stub.asInterface(b));
}
return sInstance;
}
}

...
}

       InputMethodManager用于控制显示或隐藏输入法面板及其他操作。

       同样的,给代码中加入标注。InputMethodManager和LocalBroadcastManager结构类似,这里也是在静态方法中加入线程锁进行控制。

优点

       系统中的某些对象,比如,线程池,缓存,系统级配置等等,只能有一个实例。如果不用单例模式,在每次使用的时候创建一个,这会造成资源浪费或者导致其他问题。另外,应用或多或少有一些配置类用来保存配置数据,将这些类设计成单例模式,可以确保保存的配置数据独此一份,不会有“小三小四”出现,应用也会变得稳定可靠。

参考资料

  • 《Head First设计模式》