享元模式:

介绍:
享元模式是对象池的一种实现,它的英文名叫Flyweight。
享元模式用来尽可能减少内存使用量,它适用于存在大量重复对象的场景,来缓存可共享的对象。
享元对象中的部分状态可以共享,可以共享的状态成为内部状态,内部状态不会随着环境变化;不可共享的状态成为外部状态,外部状态会随着环境的改变而改变。
在经典的享元模式中,该容器是一个Map,它的键是享元对象的内部状态,它的值是享元对象本身。客户端根据这个共享的内部状态从享元工厂中获取到享元对象。
使用场景:
(1)存在大量重复对象

(2)需要缓冲池的场景

享元模式的UML图:

举个买火车票的例子:
    
    
    
    
public interface Ticket {
public void showTicketInfo(String info);
}
     
     
     
     
public class TrainTicket implements Ticket {
 
private String from;
private String to;
//构造函数
public TrainTicket(String from,String to){
this.from =from;
this.to = to;
}
@Override
public void showTicketInfo(String info) {
int price = new Random().nextInt(200);
Log.d("Ticket",from+"-"+to+":"+price+"元");
}
}
创建火车票工厂,当需要的时候从工厂中进行获取:
    
    
    
    
public class TicketFactory {
private static Map<String,Ticket> sTicketFactory = new ConcurrentHashMap<>();
public static Ticket getTicketFromFactory(String from,String to){
String key = from+"-"+to;
if (sTicketFactory.containsKey(key)){
Log.d("Ticket","从缓存中读取");
return sTicketFactory.get(key);
}else {
Ticket ticket = new TrainTicket(from,to);
sTicketFactory.put(key,ticket);
Log.d("Ticket", "新创建一个对象");
return ticket;
}
}
}
在程序中我们只需要写:
    
    
    
    
TicketFactory.getTicketFromFactory("海南","上海").showTicketInfo("特等座");
TicketFactory.getTicketFromFactory("海南","上海").showTicketInfo("二等座");
TicketFactory.getTicketFromFactory("海南","上海").showTicketInfo("硬座");
程序的结果:
其实String也是用到了池的概念。我们知道String是存在于常量池中的,如果我们采用new String进行创建对象,那么它将新创建一个对象;如果我们直接采用String xx = "xxx"的形式,那么它将先查询常量池中有没有,如果有,则进行重用。
从Android源码理解享元模式:Message对象
我们获取Message对象的方法是obtain方法,我们点击这个方法看看:

只是一个单独的Message;
看到注释应该明白了,原来Message采用的是链表结构;
现在是进行取出,那么什么时候进行放入呢?答案就在recycle方法中:

享元模式:_第1张图片
在这里,Message承担了享元模式中三个元素的职责,既是FlyweightFactory,又是具体的对象,又是抽象的FlyWeight。
这就是享元模式,虽然它大幅度的降低了内存中对象的数量,但是也使得系统更加复杂。因为为了使对象可以共享,需要将状态外部化,这就使得逻辑变得复杂。
题外:Handler和Looper的分析
用过Handler和Thread的人应该都会知道Handler,Message,MessageQueue以及Looper,对它们的关系无非也就是Handler发送消息到MessageQueue中,然后Looper不断轮询,将消息传递给Handler,最后调用Handler的handlerMessage方法对消息进行处理。
程序一般是这么写的:

享元模式:_第2张图片
或者,我们也会采用这种:
享元模式:_第3张图片

享元模式:_第4张图片
问题来了:
(1)Handler、Looper的工作原理是什么?它们之间是怎么进行合作的?为什么Looper就能将消息准确的传递给Handler处理?
我们先来看Handler:
点击post方法进行查看:
享元模式:_第5张图片


享元模式:_第6张图片
享元模式:_第7张图片
此时,我们要执行的任务就放到了一个Message中,这个消息也被放到了消息队列中,
享元模式:_第8张图片
到底层代码中去找这个方法:
    
    
    
    
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
 
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
 
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
 
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
可知:消息队列是采用单链表的方式来进行存储消息。
如果我们采用的是第二种方法,也就是sendMessage()呢?
可以知道,底层调用的依然是sendMessageDelayed方法,之后的过程相同;
但是现在好像说的都是Handler和Message以及MessageQueue,跟Looper有什么关系? Looper又是怎么跟Handler关联上的? 而且,我们发现,

在这里出现了一个mQueue对象,也就是消息队列,它是从哪里来的?
接下来我们来看Handler的构造函数:
享元模式:_第9张图片
那么Looper是如何获取到的?为什么通过myLooper就可以获取到Looper对象?
点击myLooper方法:
享元模式:_第10张图片

原来是每一个Looper都是ThreadLocal的,也就是说每一个消息队列都只会属于一个线程。因此,如果Looper在线程A中被创建,那么只有线程A可以访问。既然有get方法,那么又是什么setLooper的呢?
我们采用sThreadLocal.set作为关键字进行查找:
在perpare方法中。这也说明了,为什么我们在子线程总创建Handler的时候要先调用Looper.prepare方法,如果不调用prepare方法,这个Handler将没有自己的Looper,也就是没有消息队列,又谈何进行处理消息。。。
但是我们在主线程中的时候并没有进行调用prepare方法呀?为什么主线程有自己的Looper,又是在什么时候创建的?我们去程序的入口ActivityThread.main中找:
享元模式:_第11张图片
我们看看prepareMainLooper都干了什么?
享元模式:_第12张图片

总结:Looper属于某个线程,消息队列存在于Looper中,因此消息队列就通过Looper跟线程关联上,而Handler又跟消息队列,Looper关联(new Handler的得到Looper跟消息队列),最终,Handler就跟消息队列,Looper和特定线程关联起来了,通过该Handler发送的消息最终就会被执行在这个线程上。


你可能感兴趣的:(享元模式:)