Android之MessageQueue源码分析(第二篇:插入Message)

Message对象插入到MessageQueue管理的单链表中

 

0、两个参数,接受一个Message对象,一个发送时间when

    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) {
            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;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                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) {
                        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;
    }

方法较长,分块分析

 

1、检查Message对象是否持有Handler对象

        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

Message对象的target字段持有的发送它的Handler对象,此处检查传入的Message对象是否持有Handler,MessageQueue要求传入的Message对象必须持有Handler对象,即target不能为null,为null,直接抛出一个IlleagalArgumentException对象,并明确告知“Message must have a target”,即Message对象必须得持有Handler对象

 

2、检查Message对象是否标记为已经使用

        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

记得在Message对象被回收的时候,特地会标记它自己的flags为已使用状态,此处会判断若Message对象是已使用状态(isInUse方法(1号知识点)),直接抛出一个IllegalStateException异常,并明确告知" This Message is already in use",那么Message对象是什么时候会是未使用状态呢?

第一 new一个Message对象的时候,它的flags默认值为0,代表未使用状态

第二 从Message类管理的消息池中获得一个Message对象时,obtain()方法(见1号知识点)会为取出的Message对象的flags赋值为0,代表是未使用状态

 

3、需先持有MessageQueue对象锁(为什么要持有MessageQueue对象锁?),检查代表MessageQueue对象的退出标志位

            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;
            }

a、mQuitting为true时,创建一个IllegalStateException对象,赋值局部变量e,这里面会产生一些打印信息,告知用户是哪个Hander对象发送的消息,在一个已经死亡的线程中在发送消息(这里主要是在日志了打印)

b、然后调用了Message对象的recycle()方法,这个方法将Message对象放回到Message类持有的消息池中(单链表做的对象复用消息池),最终返回false,代表Message对象入队失败

 

4、准备工作

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;

a、标记入队的Message对象为已使用状态,由Message对象的markInUse()方法完成

b、为Message对象持有的when赋值为传入的延迟时间when,这个延迟时间是从手机开机至今的一个延迟时间

c、定义一个局部变量p,然后取出MessageQueue对象中持有的代表当前要执行的Message对象mMessages,赋值给局部变量p

d、定义一个局部变量needWake,后面看看它要干嘛

 

5、当前没有运行的Message或者延迟时间是0或者延迟时间小于当前Message对象的延迟时间,新插入的Message对象作为头结点

            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            }

a、局部变量p代表的是头结点对象(后面用p也代表了当前结点对象),首先将插入Message对象的next指向p

b、将插入的Message对象msg作为头结点,并赋值给mMessages(作头结点)

c、MessageQueue对象持有的mBlocked,赋值给局部变量needWake(它是干嘛的?)

 

6、将插入的Message对象插入到单链表的中间(不是头结点)

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) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

a、为局部变量needWake赋值,它代表是否需要休眠,只有MessageQueue对象持有的mBlocked与当前Message对象没有持有Handler对象且新插入的消息是异步消息时,needWake才会被赋值未true

b、从第二个结点开始遍历单链表找插入的位置(因为第一个结点已经在上面先判断了),通过插入Message对象的更新时间when与每一个结点的when时间比较(当前所有结点的when值越小就越靠近单链表的头部),这里有两个可能性,第一是遍历完所有结点也没有能插入的位置,另外则是找到一个when小于某个当前结点的情况,此时遍历单链表的for循环会break,注意在每一轮:由两个局部变量prev和p负责存储代表上一个结点和当前结点

c、执行新增的Message的插入操作,新增Message对象的next指向结点p,prev的next也会指向Message对象

 

7、需要休眠的处理

            if (needWake) {
                nativeWake(mPtr);
            }

在多处看到了needWake,此时它为true时,会调用一个native方法,这个方法就是nativeWake方法(9号知识点)

 

8、插入Message成功后返回true

        return true;

 

9、native方法(为何要这么做?)

private native static void nativeWake(long ptr);

 

总结

a、当具有多个标志位需要表达的时候,总喜欢用二进制数来表达,用每一位去代表一个意思,真是厉害

b、SystemClock.uptimeMillis()表示系统开机到当前的时间总数,单位是毫秒,当系统进入深度睡眠(CPU休眠、屏幕休眠、设备等待外部输入)时间就会停止,但是不会受到时钟缩放、空闲或者其他节能机制的影响。(when的取值)

c、向MessageQueue中插入Message时,其实是在向一个单链表中插入元素

d、MessageQueue对象持有的mMessages指向的是单链表的头结点

e、插入Message时,新的Message可能作为头结点插入,也可能作为中间的结点插入(尾结点也算中间结点)

f、有个native方法nativeWake,不明白为何要让线程处于休眠状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值