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,不明白为何要让线程处于休眠状态