Android Framework 专项 - Handler(二)

android.os.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
@UnsupportedAppUsage
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}

// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}

// 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();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}

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++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
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.
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.
nextPollTimeoutMillis = 0;
}
}
重点:nativePollOnce(ptr, nextPollTimeoutMillis);

这是一个关键的方法调用,它在 Android 的消息队列 (MessageQueue) 中起到阻塞等待新消息的作用。

  1. nativePollOnce 是一个本地方法调用,它将当前线程置于休眠状态,直到满足以下任一条件:
    • 有新的消息加入消息队列。
    • 等待时间超过了 nextPollTimeoutMillis
  2. nextPollTimeoutMillis 表示下次轮询的超时时间:
    • 如果为 0,则立即进行轮询,不进入休眠。
    • 如果大于 0,则在指定的时间后唤醒线程进行轮询。
    • 如果为 -1,则线程将无限期休眠,直到有新消息加入并显式唤醒它。
  3. nativePollOnce 的底层实现通常使用 epoll 机制(在 Linux 上)。这种机制允许线程高效地等待多个文件描述符(在这种情况下是消息队列的事件描述符)的状态变化。
  4. 当线程处于休眠状态时,它不会消耗 CPU 资源,这对于节省移动设备的能量非常重要。
  5. 当有新消息发送到队列时(例如通过 Handler.sendMessage()),在 MessageQueue.enqueueMessage() 中会调用 nativeWake()nativeWake() 会向消息队列的事件描述符写入数据,从而触发 epoll 的唤醒机制,让 nativePollOnce 返回,线程继续执行后面的逻辑。

总之,nativePollOnceMessageQueue.next() 方法中的关键步骤,它通过底层的事件等待机制实现了线程的高效休眠和唤醒,从而在没有消息处理时节省资源,在有消息到达时及时响应。


重点: 同步屏障 (Sync Barrier)
1
2
3
4
5
6
7
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}

在这段代码中,涉及到了 Android 消息机制中的一个重要概念:**同步屏障 (Sync Barrier)**。

  1. 什么是同步屏障?
    同步屏障是一种特殊的消息,它的特点是其 target 属性为 null
  2. 同步屏障的作用:
    当消息队列的头部遇到同步屏障时,它会暂停处理队列中的同步消息,而只优先处理异步消息
  3. 代码逻辑分析:
    • msg != null && msg.target == null 时,说明当前队列头部是一个同步屏障。
    • 此时,代码进入 do-while 循环,目的是跳过所有同步消息,在队列中寻找下一个异步消息 (msg.isAsynchronous())。
    • 一旦找到异步消息,后续的代码逻辑将只处理这个异步消息。
    • 如果队列中没有异步消息,那么 msg 最终会变成 null,导致 nextPollTimeoutMillis 被设置为 -1,线程进入无限期休眠。
  4. 应用场景:
    同步屏障主要用于提高 UI 绘制等高优先级任务的响应速度。例如,在 View 刷新请求 (Choreographer.doFrame()) 发起时,会向消息队列发送一个同步屏障,并发送一个异步消息来执行实际的渲染任务。这样可以确保渲染任务不会被队列中积压的其他同步消息(如普通的 sendMessage)所阻塞。
  5. 移除屏障:
    当高优先级的任务执行完毕后,必须手动移除该同步屏障,否则普通消息将永远无法得到处理。

通过同步屏障,Android 系统实现了一种精细化的消息调度机制,能够确保 UI 交互的流畅性。

, ,