一、基本概念
1.1 定义
使用享元对象可有效地支持大量的细粒度的对象,达到对象共享、避免创建过多对象的效果。
享元对象内部分为两种状态:
- 内部状态:可以共享,不会随着环境变化。
- 外部状态:不可共享,随着环境的改变而改变。
在享元模式中会建立一个对象容器,经典的享元模式中该容器为一个Map
,享元模式内部一般有以下几种角色:
- 抽象享元角色:此角色是所有的 具体享元类的超类,为这些类规定出需要实现的 公共接口或抽象类。
- 具体享元角色:实现 抽象享元角色所规定的接口,享元对象的 内部状态 必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。
- 复合享元角色:代表不可以共享。
- 享元工厂:负责 创建和管理 享元角色,保证享元对象可以被系统适当地共享。
1.2 例子
享元模式1.3 应用场景
- 系统中存在大量的相似对象
- 细粒度的对象都具备较接近的外部状态,并且内部状态与环境无关
- 需要缓冲池的对象
1.4 优缺点
优点
避免在短时间内创建对象,又很快要销毁所带来的损耗。
缺点
需要维护对象,如果维护不当有可能造成内存中有大量的无用对象。
二、Android 源码
2.1 获取缓存对象
在Android
源码当中也有享元模式的应用,以我们最常用的Handler
为例,当我们发送一个消息的时候需要构建一个Message
对象,这时候我们一般会通过new Message()
方法来创建,其实有一个更好的选择,就是通过Message.obtain()
静态方法返回最近一次被回收的Message
对象,来看一下该方法的内部实现:
public final class Message implements Parcelable {
//指向下一个节点。
Message next;
//同步锁。
private static final Object sPoolSync = new Object();
//全局缓存池的头指针。
private static Message sPool;
//可用缓存池的大小。
private static int sPoolSize = 0;
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
}
在Message
的内部维护了一个通过链表来实现的缓存池,sPool
指向整个缓存池的首指针,next
指向下一个可用的Message
对象,在上面的代码当中,当发现sPool
不为空(即有可用的缓存对象),那么就取出首指针指向的对象,并将首指针向后移动一位。
2.2 存放对象
那么缓存池中的对象是什么时候被放入的呢,我们来看一下Looper#loop()
方法。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//通知 Handler 接收消息。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//回收 Message 对象。
msg.recycleUnchecked();
}
}
通过msg.target.dispatchMessage(msg)
通知Handler
后,调用Message#recycleUnchecked
来回收使用的Message
对象。
看一下里面的方法,它首先清空了Message
当中的状态变量,让它恢复到初始状态,然后将它作为新的链表首指针,并让它的next
指针指向之前的链表首指针,该链表最大的长度MAX_POOL_SIZE
为50
。
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
三、项目运用
待补充。
四、参考文献
- <<
Android
源码设计模式 - 解析与实战>>
网友评论