您的位置:首页 > 其它

享元模式:

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

使用场景:
(1)存在大量重复对象
(2)需要缓冲池的场景

享元模式的UML图:





举个买火车票的例子:

publicinterfaceTicket{
publicvoidshowTicketInfo(Stringinfo);
}


publicclassTrainTicketimplementsTicket{
privateStringfrom;
privateStringto;
//构造函数
publicTrainTicket(Stringfrom,Stringto){
this.from=from;
this.to=to;
}
@Override
publicvoidshowTicketInfo(Stringinfo){
intprice=newRandom().nextInt(200);
Log.d("Ticket",from+"-"+to+":"+price+"元");
}
}


创建火车票工厂,当需要的时候从工厂中进行获取:

publicclassTicketFactory{
privatestaticMap<String,Ticket>sTicketFactory=newConcurrentHashMap<>();
publicstaticTicketgetTicketFromFactory(Stringfrom,Stringto){
Stringkey=from+"-"+to;
if(sTicketFactory.containsKey(key)){
Log.d("Ticket","从缓存中读取");
returnsTicketFactory.get(key);
}else{
Ticketticket=newTrainTicket(from,to);
sTicketFactory.put(key,ticket);
Log.d("Ticket","新创建一个对象");
returnticket;
}
}
}


在程序中我们只需要写:

TicketFactory.getTicketFromFactory("海南","上海").showTicketInfo("特等座");
TicketFactory.getTicketFromFactory("海南","上海").showTicketInfo("二等座");
TicketFactory.getTicketFromFactory("海南","上海").showTicketInfo("硬座");


程序的结果:





其实String也是用到了池的概念。我们知道String是存在于常量池中的,如果我们采用newString进行创建对象,那么它将新创建一个对象;如果我们直接采用Stringxx="xxx"的形式,那么它将先查询常量池中有没有,如果有,则进行重用。

从Android源码理解享元模式:Message对象
我们获取Message对象的方法是obtain方法,我们点击这个方法看看:









只是一个单独的Message;





看到注释应该明白了,原来Message采用的是链表结构;

现在是进行取出,那么什么时候进行放入呢?答案就在recycle方法中:









在这里,Message承担了享元模式中三个元素的职责,既是FlyweightFactory,又是具体的对象,又是抽象的FlyWeight。

这就是享元模式,虽然它大幅度的降低了内存中对象的数量,但是也使得系统更加复杂。因为为了使对象可以共享,需要将状态外部化,这就使得逻辑变得复杂。

题外:Handler和Looper的分析
用过Handler和Thread的人应该都会知道Handler,Message,MessageQueue以及Looper,对它们的关系无非也就是Handler发送消息到MessageQueue中,然后Looper不断轮询,将消息传递给Handler,最后调用Handler的handlerMessage方法对消息进行处理。
程序一般是这么写的:









或者,我们也会采用这种:









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

















此时,我们要执行的任务就放到了一个Message中,这个消息也被放到了消息队列中,





到底层代码中去找这个方法:

booleanenqueueMessage(Messagemsg,longwhen){
if(msg.target==null){
thrownewIllegalArgumentException("Messagemusthaveatarget.");
}
if(msg.isInUse()){
thrownewIllegalStateException(msg+"Thismessageisalreadyinuse.");
}
synchronized(this){
if(mQuitting){
IllegalStateExceptione=newIllegalStateException(
msg.target+"sendingmessagetoaHandleronadeadthread");
Log.w(TAG,e.getMessage(),e);
msg.recycle();
returnfalse;
}
msg.markInUse();
msg.when=when;
Messagep=mMessages;
booleanneedWake;
if(p==null||when==0||when<p.when){
//Newhead,wakeuptheeventqueueifblocked.
msg.next=p;
mMessages=msg;
needWake=mBlocked;
}else{
//Insertedwithinthemiddleofthequeue.Usuallywedon'thavetowake
//uptheeventqueueunlessthereisabarrierattheheadofthequeue
//andthemessageistheearliestasynchronousmessageinthequeue.
needWake=mBlocked&&p.target==null&&msg.isAsynchronous();
Messageprev;
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;
}
//WecanassumemPtr!=0becausemQuittingisfalse.
if(needWake){
nativeWake(mPtr);
}
}
returntrue;
}


可知:消息队列是采用单链表的方式来进行存储消息。

如果我们采用的是第二种方法,也就是sendMessage()呢?





可以知道,底层调用的依然是sendMessageDelayed方法,之后的过程相同;

但是现在好像说的都是Handler和Message以及MessageQueue,跟Looper有什么关系?Looper又是怎么跟Handler关联上的?而且,我们发现,





在这里出现了一个mQueue对象,也就是消息队列,它是从哪里来的?
接下来我们来看Handler的构造函数:





那么Looper是如何获取到的?为什么通过myLooper就可以获取到Looper对象?

点击myLooper方法:









原来是每一个Looper都是ThreadLocal的,也就是说每一个消息队列都只会属于一个线程。因此,如果Looper在线程A中被创建,那么只有线程A可以访问。既然有get方法,那么又是什么setLooper的呢?

我们采用sThreadLocal.set作为关键字进行查找:





在perpare方法中。这也说明了,为什么我们在子线程总创建Handler的时候要先调用Looper.prepare方法,如果不调用prepare方法,这个Handler将没有自己的Looper,也就是没有消息队列,又谈何进行处理消息。。。

但是我们在主线程中的时候并没有进行调用prepare方法呀?为什么主线程有自己的Looper,又是在什么时候创建的?我们去程序的入口ActivityThread.main中找:





我们看看prepareMainLooper都干了什么?





总结:Looper属于某个线程,消息队列存在于Looper中,因此消息队列就通过Looper跟线程关联上,而Handler又跟消息队列,Looper关联(newHandler的得到Looper跟消息队列),最终,Handler就跟消息队列,Looper和特定线程关联起来了,通过该Handler发送的消息最终就会被执行在这个线程上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: