[TOC]

MessageQueue#next()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
@UnsupportedAppUsage
Message next() {

// 如果消息循环已经退出了。则直接在这里return。因为调用disposed()方法后mPtr=0
// 其实就是底层的NativeMessageQueue的应用已经被释放
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 记录空闲时处理的IdlerHandler的数量
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// native层用到的变量 ,如果消息尚未到达处理时间,则表示为距离该消息处理事件的总时长,
// 表明Native Looper只需要block到消息需要处理的时间就行了。 所以nextPollTimeoutMillis>0表示还有消息待处理
int nextPollTimeoutMillis = 0;

// 这个地方就是一个死循环.这个里面的死循环并不会造成卡死。会产生block
for (;;) {
if (nextPollTimeoutMillis != 0) {
// 刷新下Binder命令,一般在阻塞前调用
Binder.flushPendingCommands();
}
// 调用native层进行消息标示,nextPollTimeoutMillis 为0立即返回,为-1则阻塞等待。
// 就是这行代码进行消息阻塞的。这当代码会调用Native层的Looper::pollOnce()的方法.
// 如果是-1.造成阻塞等待。则会释放出CPU。
nativePollOnce(ptr, nextPollTimeoutMillis);
// 消息的遍历需要加上同步锁
synchronized (this) {
// 获取开机到现在的时间。还记得咱们执行Handler#sendMessageAtTime(SystemClock.uptimeMillis()+delay)??
final long now = SystemClock.uptimeMillis();
// 开始进行遍历队列。找到下一条要执行的消息。如果找到则返回该消息msg
Message prevMsg = null;
Message msg = mMessages;
// 如果头部消息不为null 但是是一个同步屏障消息(同步屏障消息的msg.target为null)
// 这个时候我们只能遍历同步屏障消息后面的异步消息
// 关于同步屏障的问题,我们可以参考#ViewRootImpl#scheduleTraversals的方法
// 如果是则执行循环,拦截所有同步消息,直到取到第一个异步消息为止
// 进入do while循环,遍历链表,直到找到异步消息msg.isAsynchronous()才跳出循环交给Handler去处理这个异步消息。
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
// 如果能进入这个if,则表明MessageQueue的第一个元素就是障栅(barrier)
prevMsg = msg;
msg = msg.next;
// 如果msg==null或者msg是异步消息则退出循环,msg==null则意味着已经循环结束
} while (msg != null && !msg.isAsynchronous());

}
// 找到消息队列的第一个消息。或者是屏障消息后面的第一个异步消息之后
// 下面就是看这个消息是否已经到了对应的触发时间
if (msg != null) {
// 如果当前时间now小于msg.when 说明还未到第一个可处理消息的时间
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
// 当Message还没有到可处理时间的时候,记录下一次要执行的Message的时间点
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 这种情况是正常情况下,第一个可处理消息已经到了处理时间。
// 从队列中取出该Message,并重新构建原来队列的连接关系
// Got a message.找到了可处理消息,自然就不需要block了。
mBlocked = false;
// 如果还有上一个元素 上一个元素的next(越过当前可处理消息)直接指向下一个元素
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// 如果没有上一个元素,则说明是消息队列中的头元素,直接让第二个元素变成头元素
mMessages = msg.next;
}
msg.next = null;
if (DEBUG)
Log.v(TAG, "Returning message: " + msg);
// 标记该Message为正处于使用状态,然后返回Message
msg.markInUse();
return msg;
}
} else {
// No more messages. 如果消息队列里面没有任何消息。则nextPollTimeoutMillis为-1
// 也就是上面说的nativePollOnce的长时间阻塞,让出CPU
nextPollTimeoutMillis = -1;
}

// Process the quit message now that all pending messages have been handled.
// 如果已经关闭消息队列,返回null,通知Looper停止循环。这个在主线程消息队列是不会出现的
if (mQuitting) {
dispose();
return null;
}

// 下面是关于IdleHandler的逻辑处理方法。个人认为这块逻辑其实不是next方法的实现。最好能作为一个方法抽离出现、
// 放在next方法里面增加next方法的阅读性
// 函数执行到这个地方说明两点:
// 1、消息队列中已经没有消息
// 2、消息队列中中最头部的消息都还没有到处理时间
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 如果没有等待的IdleHandler。则标记消息mBlocked。重新遍历
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// 执行到这个地方。说明有IdleHanlder插入。这个时候我们纪要创建IdleHandler
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
// 一次获取等待的IdleHandler
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
// 执行IdleHandler的queueIdle方法
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

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

// Reset the idle handler count to 0 so we do not run them again.
// 处理完IdleHandler之后将pendingIdleHandlerCount设置为0
pendingIdleHandlerCount = 0;

// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
// 处理完IdleHandler之后我们在nextPollTimeoutMillis设置完
nextPollTimeoutMillis = 0;
}
}

Next方法中,我们在遍历消息队列的时候。如果msg不为空并且target为空,说明是一个同步屏障消息。如果是则执行循环,拦截所有同步消息,直到取到第一个异步消息为止。进入do while循环,遍历链表,直到找到异步消息msg.isAsynchronous()才跳出循环交给Handler去处理这个异步消息。

如果所有的消息,都遍历完,没有找到异步消息的话,我们就会nextPollTimeoutMillis设置为-1 及永久阻塞住next的方法。知道我们有新的消息插入的时候,我们调用native层的方法nativeWake(mPtr)进行唤醒。然后才会继续执行。