当前位置: 首页 > article >正文

MessageQueueMessage Pool—消息的读写、删除和回收

如有源码,版本为android6.0

消息的读写和删除对应的类是MessageQueue,这里的写也伴随着删除,回收有个抽象的概念Global Pool或者Message Pool,前者obtain()方法注释有提到,后者我感觉更形象。当然回收是在Looper的loop()方法中。

1、MessageQueue的数据结构

翻译为消息队列,但实际的数据结构是单向链表结构,下文中仍以消息队列称之;

Message中有个重要的属性Message next;next指向另外一个Message。在MessageQueue中Message A的next指向B,B的next指向C;这样 A B C就组成了单向链表结构,它们是以时间排序的,其中A为header,表示最先要处理的消息,C是最后要处理的消息,这个header是MessageQueue的一个属性,这样有了这个Header就拥有整个消息链表。




2、MessageQueue三种操作

插入、获取、删除;对应方法为enqueueMessage()、next()、removeMessage()

2.1插入消息

Handler的send()系列方法最终调用的就是enqueueMessage()

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) {//同步锁,插入、删除和获取都是用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;//队的条件就这个when,意思距离处理的时间,数值越大排队越靠后。Message p = mMessages;//mMessages就是队列的头boolean needWake;if (p == null || when == 0 || when < p.when) {//p==null说明队列为空,when==0意思是现在就要处理,没有任何延迟,when<p.when,说明比
//现在的header还要早处理。// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;//换新header,新的header的next指向原来的header。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) {//p==null说明到了队列的尾部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;}

这个方法的简单描述就是: 按照时间顺序,将消息插入消息队列中,有可能是头,有可能是中间,也有可能是尾部。


2.2获取消息

获取消息是在Looper的loop()循环中
Message next() {// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported.final long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {//容错? target不会为null// Stalled by a barrier.  Find the next asynchronous message in the queue.do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// Next message is not ready.  Set a timeout to wake up when it is ready.nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// Got a message.mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;if (DEBUG) Log.v(TAG, "Returning message: " + msg);msg.markInUse();return msg;}} else {// No more messages.nextPollTimeoutMillis = -1;}// Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// No idle handlers to run.  Loop and wait some more.mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// Run the idle handlers.// We only ever reach this code block during the first iteration.for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0;}}
   这个方法意思是说:取链表的header,如果这个消息时间不满足,线程就阻塞,等时间到了就取出这个消息,然后第二个消息作为header。也有可能新插入了一个消息唤醒了线程,继续循环判断,直到取出一个消息为止。

2.3删除消息

removeMessages(Handler h, int what, Object object) 和removeMessages(Handler h, Runnable r, Object object)  
这里不做过多的解读了;简单概括还是对消息队列的遍历,找到符合条件的Message,删除这个Message,删除的Message是要被回收的,回收的消息放入消息池中。


3、消息池

消息池没有对应的类,在SDK中以global pool称呼。消息池中的消息数据结构是

   private static final Object sPoolSync = new Object();// 消息池操作,同步锁类 private static Message sPool;// 消息池的栈顶private static int sPoolSize = 0;//消息池的消息个数 private static final int MAX_POOL_SIZE = 50;//消息池的消息个数上限

上面是Message中属性。

3.1提供消息—出栈

  /*** 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 flagsPoolSize--;//消息池数量减少一个return m;//}}return new Message();//消息池没有消息 则new一个。}

3.2 消息回收—压栈

 /*** 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;//压栈,原来栈顶的作为本消息的next,即第二个消息sPool = this;sPoolSize++;}}}

从上面两个操作可以看出, MessageQueue中的消息和MessagePool中的Message之间的连接的方式是一样的,都是通过next指向下一个Message,但MessageQueue的数据结构是链表,但MessagePool是栈。


http://www.lryc.cn/news/2416428.html

相关文章:

  • 编程算法常用术语中英对照
  • 正则表达式不包含指定字符
  • 着陆页如何设计?详解着陆页设计的5大要点及常见问题
  • nonzero用法
  • 【视频编码学习】AVS3参考软件HPM-4.1简单配置运行
  • 路由策略——route map
  • 非阻塞connect
  • EnableWindow()函数的应用
  • (28)CreateFont函数
  • C# CultureInfo 类之各国语言所对应的的区域性名称
  • JDBC元数据操作(一)-- DatabaseMetaData接口详解
  • 小菜和大鸟的编程故事之三:代码规范和重构意识
  • 数据库系统原理与应用教程(029)—— MySQL 的数据完整性(二):定义主键(primary key)
  • VMware虚拟机安装Ubuntu14.04.5-server详细图文教程
  • JS window对象 返回前一个浏览的页面 back()方法,加载 history 列表中的前一个 URL。 语法: window.history.back();
  • HDTUNE工具下载
  • python 爬取google总结
  • [PaddleGAN]人脸表情迁移-视频换脸
  • Android逆向之旅---破解一款永久免费网络访问工具
  • 关于同步电机的Ldq测量
  • 域名系统(Domain Name System,DNS)
  • 安奈特智能技术-半导体制造行业RFID解决方案
  • folsom版本horizon架构剖析
  • 分享35款最新出炉的免费个人博客模板
  • java中的强引用(Strong reference),软引用(SoftReference),弱引用(WeakReference),虚引用(PhantomReference)
  • session.setAttribute和request.setAttribute的区别
  • 白盒模型和黑盒模型
  • RT-Thread : IEEE1588/PTP 协议的实现
  • 二、Linux开发中常用到的命令
  • C#学习教程14——进程与线程