Handler---4部曲---3. MessageQueue队列

Handler---4部曲---1. 总体流程
Handler---4部曲---2. ThreadLocal 存储主流程
Handler---4部曲---3. MessageQueue队列
Handler---4部曲---4.细节补充

本章内容

MessageQueue队列.png

一. 数据结构

MessageQueue数据结构.png

MessageQueue 3个成员变量

  ArrayList<IdleHandler> mIdleHandlers   --->
  boolean mBlocked;  --->next()是否阻塞 标记
  Message mMessages;  --->队列头

Message 3个成员变量

   Handler target; //当前handler
   long when;  //消息延迟时间
   Message next;//下一个消息

二. 消息分类

2.1 同步消息
2.2 异步消息
2.3 同步屏障消息 --->撤销屏障removeSyncBarrier()
2.4 空闲消息

三. 4种消息的添加方式

MessageQueue消息添加方式.png

3.1 同步消息
enqueueMessage(Message msg, long when) 添加

boolean enqueueMessage(Message msg, long when) {
        //用此方法添加到队列, msg一定要给target赋值
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            msg.when = when;
            Message p = mMessages;
            //如果 首次进来 || 时间=0 || 当前时间<队列头的时间
           //就把当前消息加到队列头
            if (p == null || when == 0 || when < p.when) { //
                msg.next = p;
                mMessages = msg;
            } else {
                //把当前msg按照when 从小到大插入队列中
                Message prev;
                for (;;) {
                    prev = p; //
                    p = p.next;
                    if (p == null || when < p.when) { 
                        break;
                    }
                }
                //此时  prev.when<msg.when<p.when
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        return true;
    }

总结:把msg消息按时间顺序, 从小到大添加到队列中.

3.2 异步消息
和同步消息差不多, 只是把msg标记为异步.

 msg.setAsynchronous(true)  
 MessageQueue.enqueueMessage(msg, 时间xxx)

3.3 同步屏障消息

//添加同步屏障消息 
int token = MessageQueue.postSyncBarrier();
//移除同步屏障消息
removeSyncBarrier(token);
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        synchronized (this) {
            //获取Message 对象
            final Message msg = Message.obtain(); 
            msg.when = when; //时间赋值---当前时间
            // 以下的操作,和普通消息一样,按时间顺序 小到大排列
           // 最后msg就是队列头.
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

总结:

msg内部创建,也是按时间顺序进行排列的,
msg.target = null
msg.when = 当前时间

3.4 空闲消息
将消息添加到mIdleHandlers集合中

    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

四. next()消息处理

MessageQueue next出队流程.png
    Message next() {
        //mIdleHandlers集合数量
        int pendingIdleHandlerCount = -1;
        //阻塞休眠时间
        int nextPollTimeoutMillis = 0;
        for (;;) {
            //阻塞队列   nextPollTimeoutMillis休眠时间
            // -1 一直休眠, 等待唤醒
            // 0  不休眠, 马上执行
            // 大于0,休眠时间
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages; 
                //判断是否有异步消息,  如果有,循环队列, 取出异步消息
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
               //注意:如果异步消息 的时间  > 当前时间, 则会一直等待, 其它消息得不到执行, 直到异步消息执行完为止.
                
                //此时msg代表取出的消息
                if (msg != null) { //有消息
                    if (now < msg.when) { //没有到执行时间
                        //休眠时间 = (时间差,Integer.MAX_VALUE)中的小值.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 马上执行消息
                        mBlocked = false; //标记线程不阻塞
                        if (prevMsg != null) { //异步消息时
                            prevMsg.next = msg.next;
                        } else { //同步消息时,队列头=下一个msg
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        return msg;
                    }
                } else {
                    // 如果没有消息,休眠时间--->一直休眠
                    nextPollTimeoutMillis = -1;
                }

                //是否退出(下一篇会讲)
                if (mQuitting) {
                    dispose();
                    return null;
                }


//>>>>>>>>>>>>>>>下面是处理 空闲消息 mIdleHandlers>>>>>>>>>>>>>>>>>>>>>>>>>>
                if (pendingIdleHandlerCount < 0  //首次进入
                        //&& (没有消息 || 当前时间 < 队列头时间)
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //没有空闲消息处理
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;  //标记,线程阻塞
                    continue;
                }
               //空闲消息List 转 数组
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
           }
            
            //循环处理空闲消息
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // 释放数组内存

                boolean keep = false;
                try {
                    // //返回false才会删除任务, 否则重复执行
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            //重置变量
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }
Message消息处理:
  1. 首先获取队列头:Message msg = mMessages;
  2. 判断是否有同步屏障的消息. 如果有,会循环队列, 去找异步消息msg
    ①. 如果有异步消息,msg = 异步消息
    ②. 如果没有异步消息,msg==null.
  3. 如果msg==null, 线程进入休眠,nextPollTimeoutMillis = -1;
  4. 如果 msg!=null;
    ①. 当前时间 < msg.when, nextPollTimeoutMillis(休眠时间) = msg.when-当前时间
    ②. 当前时间 >= msg.when,取出msg给loop去处理
    ------>同步消息时,队列头=下一个msg, mMessages = msg.next
空闲消息处理:

没有msg消息处理的时候,才会处理空闲消息

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

推荐阅读更多精彩内容