内容简介:这是“Android消息机制”系列的第一篇文章,系列文章目录如下:将Android概念带入后,就变成了Android消息机制的故事:
这是“Android消息机制”系列的第一篇文章,系列文章目录如下:
消息机制的故事
寿司
陈放在 寿司碟
上, 寿司碟
按先后顺序被排成 队列
送上 传送带
。 传送带
被启动后, 寿司
挨个呈现到你面前,你有三种享用寿司的方法。
将Android概念带入后,就变成了Android消息机制的故事:
消息(Message) 消息队列(MessageQueue) 消息泵 (Looper)
暂未找到 Handler
在此场景中对应的实体。它是一个更抽象的概念,它即可以生产寿司,又把寿司送上传送带,还定义了怎么享用寿司。暂且称它为 消息处理器
吧。
如果打算自己开一家回转寿司店,下面的问题很关键:
- 如何生产寿司(如何构造消息)
- 如何分发寿司(如何分发消息)
让我们带着这两个问题,去分析一下消息机制源码。 (ps: 下文中的 粗斜体字 表示引导源码阅读的内心戏)
如何构造消息
寿司碟是重复利用的,享用完寿司后,它被清洗,然后被存放起来,以便再次利用。没有哪个老板会在用餐后把寿司碟销毁,下次要用就买新的,这样代价太大。 同样道理, 构造消息对象代价也很大,它是否也像寿司碟一样可以复用?如果是,那消息存放在哪里? 让我们以 Handler.obtainMessage()
为切入点一探究竟:
public class Handler { ... public final Message obtainMessage(){ return Message.obtain(this); } ... } public final class Message implements Parcelable { ... /** * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned. * @param h Handler to assign to the returned Message object's <em>target</em> member. * @return A Message object from the global pool. */ public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; } ... } 复制代码
- 其中
Message.target
是一个Handler
类型的成员变量。 为啥Message要记录构造它的Handler对象? 好问题!但这个问题的解答需要等到分析消息处理的时候才能解答,先留个悬念。 - 构造消息调用链的终点是
Message.obtain()
,源码如下:
public final class Message implements Parcelable { //省略了非关键代码 ... // sometimes we store linked lists of these things //指向消息链上下一个消息的引用 /*package*/ Message next; //消息链头部引用,它是静态的,可以被所有消息对象共享 private static Message sPool; //消息链长度 private static int sPoolSize = 0; ... /** * 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) { //1. 定义指向消息链头部引用 Message m = sPool; //2. 定义新的消息链头部 sPool = m.next; //3. 断链 m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; //返回消息链头部消息 return m; } } //如果消息链为空则新建消息 return new Message(); } ... } 复制代码
- 如果对数据结构中的
链表
还有映像,obtain()
就是在取链表头。图示如下:
-
消息池是用链表结构实现的。那
Message
一定有一个指向后续结点的“指针” ,果不其然,在其成员变量中找到Message next;
。 -
消息池头指针
sPool
是一个Message
类型的静态变量,这表示所有Message
都共享这一个消息池。 -
obtain()
是从消息池中拿消息, 那一定还有一个方法是往池里填消息 ,在Message
类中搜索sPool
使用的地方,找到如下这个方法:
/** * 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) { //1. 回收的消息接入消息链 next = sPool; //2. 回收的消息成为消息链新头部 sPool = this; sPoolSize++; } } } 复制代码
- 正如猜想的那样
recycleUnchecked()
会将当前消息插入到消息链头部。图示如下
- 读到这里,我们知道“消息从池中来最终又回到池中去”, 那到底消息是在什么时候才会被回收到消息池呢? 好问题!这个问题要等分析完消息分发才能解答。但现在我们可以大胆的猜测一下: 承载寿司的碟子会在寿司被享用完之后被厨房回收,那消息是不是再被处理完之后就被回收了?
以上所述就是小编给大家介绍的《回转寿司你一定吃过!——Android消息机制(构造)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。