享元模式
定义:
使用共享对象能够有效的支持大量的细粒度的对象html
应用场景
系统中存在大量的类似对象
细粒度的对象都具有比较接近的外部状态,而内部状态与环境无关,也就是说对象没有特定身份
须要缓冲池的场景
享元模式的写法
UML图以下:
java
Flyweight:享元对象抽象基类或者接口
ConcreteFlyweight:具体的享元对象
FlyweightFactory:享元工厂,负责管理享元对象池和建立享元对象
以买火车票为例说明,咱们知道买票的时候,客户端每次刷新就会返回一个结果,当有不少人同时抢票的时候,如果每次都建立一个新结果对象返回,那么就会形成CPU占用率居高不下,GC任务繁重,颇有可能形成OOMweb
定义抽象享元设计模式
//火车票信息
public interface Ticket {
public void showTicketInfo(String bunk);
}
建立具体享元对象缓存
class TrainTicket implements Ticket{
public String from; //始发站
public String to; //终点站
public String bunk; //铺位
public int price;
public TrainTicket(String from,String to) {
this.from = from;
this.to = to;
}
@Override
public void showTicketInfo(String bunk) {
price = new Random().nextInt(300);
System.out.println("购买从"+from+"到"+to+"的"+bunk+"火车票"+",价格:"+price);
}
}
建立享元工厂dom
//这种每次都是返回一个新的对象,其中会形成大量重复的对象,不可取
public class TicketFactory{
public static Ticket getTicket(String from,String to) {
return new TrainTicket(from,to);
}
}
修改以下ide
public class TicketFactory{
private static Map sTicketMap = new ConcurrentHashMap<>();
public static Ticket getTicket(String from,String to) {
String key = from+"-"+to;
if (sTicketMap.containsKey(key)) {
//使用缓存
return sTicketMap.get(key);
} else {
//新建结果对象,存入缓存
Ticket ticket = new TrainTicket(from,to);
sTicketMap.put(key,ticket);
return ticket;
}
}
}
这里就是所说的享元模式经过消息池的形式有效的减小重复对象的存在,它经过内部状态表示某个种类的对象,外部程序根据这个不会变化的内部状态今后消息池中取出对象,使得这一类的对象能够重复使用,例如1000我的查询从北京到太原的火车票,经过享元模式就能够将1000个结果对象减小至1个!在这个例子中不变的内部状态是出发地和目的地,变化的外部状态是铺位和价格。svg
Handler原理图
其实Message、MessageQueue、Looper、Handler的工做原理像是生产线,Looper是发动机,MessageQueue是传送带,Handler是工人,Message就是待处理的产品。咱们知道Android应用是事件驱动,每一个事件都会转化为一个系统消息,即Message。消息中包含了事件的信息和发送消息的处理人Handler,每一个进程中都有一个默认的消息队列,也就是MessageQueue,这个消息队列维护了一个待处理的消息列表,有一个消息循环不停的从这个队列中取出消息,处理消息,这样就使应用动态的运做起来了。那么Message就会产生不少的对象,由于整个应用都是由事件,也就是Message来驱动的,系统须要不断的产生Message、处理Message、销毁Message,这种重复大量的构建Message就是利用了享元模式oop
Handler的两种使用方式:post、sendMessage,咱们就post来讲明post
/**
sendMessage中传入的参数是Message对象,而post中传入的参数是Runnable对象,值得注意的是此处是调用
getPostMessage()方法将Runnable对象封装到一个Message对象
*/
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
getPostMessage(Runnable r)
private static Message getPostMessage(Runnable r) {
//注意这里的Message对象的获取方式,稍后再说
Message m = Message.obtain();
m.callback = r;
return m;
}
咱们接着向下看,直到sendMessageAtFrontOfQueue方法
//Message被压入MessageQueue队列中
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
看过怎么将Message压入队列,咱们来看一看怎么从队列中取出Message
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;
......
//死循环,不断的从队列中取Message
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
......
try {
//注意这句,是真正执行Message
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
msg.recycleUnchecked();
}
}
dispatchMessage源码
public void dispatchMessage(Message msg) {
//注意上述在使用post方式的时候调用getPostMessage
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handleCallback
//执行Message
private static void handleCallback(Message message) {
message.callback.run();
}
咱们说Message是享元模式在这里的一个应用,那么反过头来去看一看Message的获取方式
Message m = Message.obtain();
Message#obtain
public static Message obtain() {
synchronized (sPoolSync) {
/**请注意!咱们能够看到Message中有一个next字段指向下一个Message,这里就明白了,Message消息池中
没有使用Map这样的容器,而是使用的链表!
*/
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对象,但是在哪里放Message到链表中呢?在recycle方法中
Message#recycle
public void recycle() {
//判断消息是否还在使用
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
//清空状态,将消息添加到消息链表中
recycleUnchecked();
}
recycleUnchecked
void recycleUnchecked() {
//清除状态,设置该消息in_use flag
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++;
}
}
}
Message内部维护一个尺度为50的链表去管理被回收的Message的对象,调用obtain方法时会优先从消息池中取Message对象,如果没有可复用的Message,就会建立该Message,以后回收时会被存放在消息池中,下次调用obtain时就能够被复用,可是这里不是经典的享元模式,更像是一个对象池,由于没有内部、外部状态,不过值得关注的是灵活运用模式,
public static Message obtain() {
synchronized (sPoolSync) {
//优先从池中获取Message,
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();
}
享元模式的有点在于能够大幅度的下降内存中的对象的数量,可是这样一来系统的复杂程度就提升了,同时享元模式将享元模式的状态外部化,而读取外部状态是运行时间稍微变长