一定搞懂Handler消息处理机制系列之「02.Message入列」

Message入列

判断新创建Message处于队列中的位置,并插入相应位置

//截取自MessageQueue.enqueueMessage()方法来举例(删除了部分与此次无关代码)
boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
              //标记传入的msg被使用
            msg.markInUse();
            msg.when = when;
              //创建临时变量来储存消息队列中的Message对象
            Message p = mMessages;
            boolean needWake;
                 /*   
                  * 当消息队列中没有消息
                  * 或传入Message的触发时间为0时
                * 或传入Message的触发时间小于当前消息队列中的Message的触发时间
                */
            if (p == null || when == 0 || when < p.when) {
                //把传入的Message放入当前消息队列中的Message之前
                msg.next = p;
                  //把当前消息队列中的Message对象重置为传入的Message对象
                mMessages = msg;
                needWake = mBlocked;
            }  else {
               /* 
                  * 当消息队列中有消息
                  * 且传入Message的触发时间不为0时
                * 且传入Message的触发时间大于当前消息队列中的Message的触发时间
                */
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
              //创建一个临时变量
                Message prev;
                for (;;) {
                      //储存临时变量p(当前消息队列中的Messge)
                    prev = p;
                      //让p指向自己在消息队列中的下一条消息
                    p = p.next;
                      //当p为null时,说明prev是当前消息队列中的最后一条消息
                      //或者传入Message的触发时间小于p的触发时间时终止循环
                    if (p == null || when < p.when) {
                        break;
                    }
                }
                  /* 此时的p满足以下两个条件中的一个:
                   *  1.p为null时,说明prev是当前消息队列中的最后一条消息(因为p为null,所以prev不为
                   null且prev的触发时间小于传入Message的触发时间,所以传入Message的为消息队列中的最
                   后一条消息,prev为传入Message的上一条消息)
                   *  2.p的触发时间大于传入Message的触发时间(因为p的触发时间大于传入Message的触发时
                间,所以p在消息队列中是传入Message的下一条消息,因为在上一次循环中没有进入if语句,
                  所以prev不为null且触发时间小于传入Message对象的触发时间,所以prev在消息队列中处于
                   传入Message的上一条)
                   */ 
                msg.next = p;
                prev.next = msg;
            }
        }
        return true;
    }

Message的获取方式

Message的获取方式除了new Message这种方式,Message类还提供了obtain方法来获取Message

//Message类中有一个静态全局变量来储存空闲或者回收的Message对象
private static Message sPool;
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; 
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

这种方式是把静态全局变量sPool(这里可以把这个Message看做当前消息池中的第一条消息)标记为未使用然后返回,如果sPool为null才会创建新的Message对象,这样不会造成资源的浪费,避免创建太多Message对象。关于为什么sPool会是被回收的Message对象,上源码:

      //此方法为Message的回收方法
  public void recycle() {
          //在回收的方法
        recycleUnchecked();
    };
  void recycleUnchecked() {
          //这里在做一些重置的工作
        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) {
                  //重点!!!
                  /*把当前消息池中第一条消息(也就是sPool)置为当前消息的下一条消息(sPool为
              全局静态变量,所有Message都共用这一个sPool)*/
                next = sPool;
                  /*把当前消息置为消息池中第一条消息(因为上一步骤已经把原来消息池中的第一条消息置为
              了当前消息的下一条消息,现在把当前消息置为消息池中的第一条消息,所以sPool永远代表
              消息池中的第一条消息)*/
                sPool = this;
                sPoolSize++;
            }
        }
    }

可以看出Message在回收过程中,只要消息池的数量小于消息池的最大容量时,就是把当前Message放入消息池中。

Message在MessageQueue队列中存在的形式

从Message入列方式我们也看出,再有新消息进入队列时,是先判断新消息的触发时间,找出消息应该插入消息队列的位置,把这个位置的消息的next置为本条新消息,然后把新消息的next置为这个位置的消息的下一条消息。类似以下结构(如果我理解有错,欢迎指出)。

message
message

系列目录:

一定搞懂Handler消息处理机制系列之「01.Handler消息发送」

一定搞懂Handler消息处理机制系列之「02.Message入列」

一定搞懂Handler消息处理机制系列之「03.MessageQueue与Looper的由来」

一定搞懂Handler消息处理机制系列之「04.Message是如何触发的」

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容