概述
Android 消息機制主要指的是 Handler 的運行機制及其所依賴的 MessageQueue 和 Looper 的工作過程,Handler、MessageQueue、Looper組成一個相互聯繫的整體。本文先從 MessageQueue 的源碼來說明其實現原理。
MessageQueue 原理
MessageQueue ,顧名思義,意爲消息隊列,其操作主要有插入和讀取。插入對應的方法爲 enqueueMessage()
,即往消息隊列中插入一條消息,而讀取對應next()
,該方法會從消息隊列中取出一條消息並將其從消息隊列中刪除。雖然 MessageQueue 的名字包含隊列(Queue),但是其底層實現採用的是單鏈表,這是因爲鏈表在插入和刪除方面的性能好。下面看其源碼實現。
boolean enqueueMessage(Message msg, long when) {
... ...//省略
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;
}
enqueueMessage()中,Message 對象 p 代表下一個消息,對於新進來的消息,如果滿足條件: p == null || when == 0 || when < p.when
,那麼就將它插在消息列表的最前面;否則,就按照消息觸發的時間( when 字段)來插入消息。也就是說,消息的插入是按照時間順序從小到大進行的。
接下來在看看獲取消息的 next()
方法:
Message next()
.....//省略
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// 1.如果nextPollTimeoutMillis=-1,一直阻塞不會超時。
// 2.如果nextPollTimeoutMillis=0,不會阻塞,立即返回。
// 3.如果nextPollTimeoutMillis>0,最長阻塞nextPollTimeoutMillis毫秒(超時)
// 如果期間有程序喚醒會立即返回。
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;
//如果target==null,那麼它就是屏障,需要循環遍歷,一直往後找到第一個異步的消息
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) {
//如果有消息需要處理,先判斷時間有沒有到,如果沒到的話設置一下阻塞時間,
//場景如常用的postDelay
if (now < msg.when) {
//計算出離執行時間還有多久賦值給nextPollTimeoutMillis,
//表示nativePollOnce方法要等待nextPollTimeoutMillis時長後返回
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲取到消息
mBlocked = false;
//鏈表操作,獲取msg並且刪除該節點
if (prevMsg != null)
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
//返回拿到的消息
return msg;
}
} else {
//沒有消息,nextPollTimeoutMillis復位
nextPollTimeoutMillis = -1;
}
.....//省略
}
先說一下,MessageQueue 的數據結構爲一個單向鏈表,Message 對象有個 next 字段保存列表中的下一個,MessageQueue 中的 mMessages 保存鏈表的第一個元素。
可以看到,next()
是一個無限循環的方法,讀取消息時如果有消息就將該消息從消息列表中移除並返回該消息,否則就一直阻塞。
參考: