享元模式

定义:

使用共享对象可以有效的支持大量的细粒度的对象

应用场景

  • 系统中存在大量的相似对象
  • 细粒度的对象都具备比较接近的外部状态,而内部状态与环境无关,也就是说对象没有特定身份
  • 需要缓冲池的场景

享元模式的写法

UML图如下:

  • Flyweight:享元对象抽象基类或者接口
  • ConcreteFlyweight:具体的享元对象
  • FlyweightFactory:享元工厂,负责管理享元对象池和创建享元对象

以买火车票为例说明,我们知道买票的时候,客户端每次刷新就会返回一个结果,当有很多人同时抢票的时候,若是每次都创建一个新结果对象返回,那么就会造成CPU占用率居高不下,GC任务繁重,很有可能造成OOM

定义抽象享元

//火车票信息
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;}@Overridepublic void showTicketInfo(String bunk) {price = new Random().nextInt(300);System.out.println("购买从"+from+"到"+to+"的"+bunk+"火车票"+",价格:"+price);}
}

创建享元工厂

//这种每次都是返回一个新的对象,其中会造成大量重复的对象,不可取public class TicketFactory{public static Ticket getTicket(String from,String to) {return new TrainTicket(from,to);}}

修改如下

 public class TicketFactory{private static Map<String ,Ticket> 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个!在这个例子中不变的内部状态是出发地和目的地,变化的外部状态是铺位和价格。

Handler原理图


其实Message、MessageQueue、Looper、Handler的工作原理像是生产线,Looper是发动机,MessageQueue是传送带,Handler是工人,Message就是待处理的产品。我们知道Android应用是事件驱动,每个事件都会转化为一个系统消息,即Message。消息中包含了事件的信息和发送消息的处理人Handler,每个进程中都有一个默认的消息队列,也就是MessageQueue,这个消息队列维护了一个待处理的消息列表,有一个消息循环不停的从这个队列中取出消息,处理消息,这样就使应用动态的运作起来了。那么Message就会产生很多的对象,因为整个应用都是由事件,也就是Message来驱动的,系统需要不断的产生Message、处理Message、销毁Message,这种重复大量的构建Message就是利用了享元模式

Handler的两种使用方式:post、sendMessage,我们就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;......//死循环,不断的从队列中取Messagefor (;;) {Message msg = queue.next();if (msg == null) {return;}......try {//注意这句,是真正执行Messagemsg.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方式的时候调用getPostMessageif (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}

handleCallback

 //执行Messageprivate 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 flagsPoolSize--;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 flagflags = 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 flagsPoolSize--;return m;}}//没有可复用的对象,就重新构建return new Message();
}

享元模式的有点在于可以大幅度的降低内存中的对象的数量,但是这样一来系统的复杂程度就提高了,同时享元模式将享元模式的状态外部化,而读取外部状态是运行时间稍微变长

Android源码设计模式解析与实战
https://www.cnblogs.com/zzfsblog/p/4248836.html

Android源码看设计模式(十)--------关于享元模式的Handler相关分析相关推荐

  1. GoF设计模式(十二) - 享元模式

    前言 享元模式(Flyweight Pattern)运用共享技术有效地支持大量细粒度的对象,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的 ...

  2. Android源码看设计模式(十二)--------关于观察者模式的Rxjava的相关分析

    观察者模式 定义: 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并且被自动更新 使用场景 关联行为场景,注意不是"组合"关系 事件多级 ...

  3. 设计模式(十)享元模式Flyweight(结构型)

    设计模式(十)享元模式Flyweight(结构型) 说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释 ...

  4. 结构型设计模式(五) 之 享元模式是池技术的表现

    1 定义 享元模式(Flyweight Pattern)属于结构型设计模式之一,它主要是使用共享对象有效地避免了创建过多的性质相近的对象,从而降低内存的占用,提高程序的性能.它也是池技术的重要实现方式 ...

  5. 结合JDK源码看设计模式——桥接模式

    前言: 在我们还没学习框架之前,肯定都学过JDBC.百度百科对JDBC是这样介绍的[JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Jav ...

  6. 【设计模式自习室】享元模式 Flyweight Pattern:减少对象数量

    前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该模式的介绍,包括: 引子.意图(大白话解释) 类图.时序图(理论规范) 该模式的代码示例:熟悉该模 ...

  7. 设计模式八(享元模式)

    享元模式 面向对象技术可以很好地解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数.当对象数量太多时,将导致运行代价过高,带来性能下降等问题.享元模式正是为解决这一类问题而诞生 ...

  8. 【设计模式】11.享元模式

    概述 定义: ​ 运用共享技术来有效地支持大量细粒度对象的复用.它通过共享已经存在的对象来大幅度减少需要创建的对象数量.避免大量相似对象的开销,从而提高系统资源的利用率. 结构 享元(Flyweigh ...

  9. java23设计模式---class10、享元模式(FlyWeight)

    文章目录 一.基本介绍 1.定义 2.优点 3.缺点 4.角色 1)抽象享元类 2)具体享元类 3)享元工厂类 4)组合享元类 5.内部状态和外部状态 二.应用情景 1.线程池 2.String 3. ...

最新文章

  1. pycharm配置python解释器_Python大佬手把手教你进行Pycharm活动模板配置
  2. linux离线安装服务 =====Ubuntu16.0.4 离线部署Openssh
  3. 小米air耳机重新配对_小米发布 399 元真无线蓝牙耳机,除了小爱同学还支持其他手机语音助手...
  4. CodeForces - 351E Jeff and Permutation(贪心)
  5. AndroidStudio+ideasmali动态调试smali汇编
  6. 用于故障诊断的残差收缩网络
  7. HBase中数据的多版本特性潜在的意外
  8. HashTable、HashSet和Dictionary的区别
  9. python 字典写入excel_Python向excel中写入数据的方法
  10. 【TOGAF】DAY 1:如何通过 TOGAF 9 认证
  11. Matlab2018破解方法
  12. Linux centos 7安装
  13. 快速输入对号框(框中加对号或者对勾)的方法
  14. TortoiseSVN环境搭建以及局域网服务器
  15. 通过无线网络实现两台计算机共享打印机共享,同一WiFi环境中两台电脑共享打印机技巧方法...
  16. IJCAI21审稿机制介绍:提交时间、提交流程、注意事项等
  17. 全球与中国轨道交通受电弓滑块市场深度研究分析报告
  18. chatgpt接入微信
  19. 网友测试:优品拍拍二手交易平台
  20. networkx教程

热门文章

  1. linux红帽7修改时间,RedHat Linux 修改时区
  2. xlrd.biffh.XLRDError:Unsupported format,or corrupt file:Expected BOF record;found b‘ ‘
  3. 刮分10万奖金池,FinClip 小程序插件开发大赛有的搞
  4. Mendeley+坚果云实现文件和备注同步
  5. mysql eav_mysql – 数据库设计:EAV选项?
  6. 优思学院|PDCA、A3、DMAIC、8D 有什么区别?
  7. 计蒜课/百度的年会游戏(枚举)
  8. 【速学java】 java后台框架 springmvc整合mybatis框架源码
  9. linux 下录音alsa介绍
  10. R 获取exon长度