概述
Android 的消息處理機(jī)制主要是指 Handler 的運(yùn)行機(jī)制以及 Handler 所附帶的 MessageQueue 和 Looper 的工作流程喇伯。
在 Handler 創(chuàng)建完畢之后,就可以通過(guò) Handler.post 方法將一個(gè) Runnable 轉(zhuǎn)換成一個(gè) Message 對(duì)象,它會(huì)調(diào)用 MessageQueue 的 enqueueMessage() 將其放入消息隊(duì)列中:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
Looper 發(fā)現(xiàn)有新消息到來(lái)時(shí)旁振,就會(huì)處理這個(gè)消息谓罗,最終消息中的 Runnable 或 Handler 的 handleMessage 方法會(huì)被調(diào)用域那。這樣就切換到了創(chuàng)建 Handler 所在的線程中去執(zhí)行了活逆。
其工作流程圖如下所示:
MessageQueue 的工作原理
MessageQueue 類主要包含兩個(gè)操作:插入和讀取,它通過(guò)一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表剪决,在每個(gè) Looper 中都持有一個(gè) MessageQueue 對(duì)象灵汪。
enqueueMessage 方法主要就是往單鏈表中插入一條消息,我們來(lái)看一下 next 讀取方法的代碼:
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;
}
}
盡管代碼偏長(zhǎng)柑潦,但可以看出 next 方法實(shí)際上是一個(gè)無(wú)限循環(huán)享言,直到 MessageQueue 中有消息則將其取出并刪除,否則會(huì)一直阻塞在這里渗鬼。
Looper 的工作原理
Looper 在消息處理機(jī)制中負(fù)責(zé)不斷從 MessageQueue 中查看是否有新消息览露,如果有的話則進(jìn)行處理,否則就一直阻塞在哪里乍钻。
在初始化 Looper 時(shí)會(huì)創(chuàng)建一個(gè) MessageQueue:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
當(dāng)調(diào)用 prepare 方法時(shí)肛循,可當(dāng)前線程初始化并綁定一個(gè) Looper,可以看到不能重復(fù)調(diào)用 prepare 方法:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
最后我們會(huì)調(diào)用 loop 方法來(lái)開啟消息循環(huán)银择,這也是 Looper 中最重要的一個(gè)方法,我們先列出整體代碼累舷,再來(lái)依次進(jìn)行分析:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
先取出與當(dāng)前線程綁定的 Looper浩考,并取出 Looper 中的 MessageQueue 消息隊(duì)列,接著就進(jìn)入了無(wú)限循環(huán):
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
后面的代碼都是在無(wú)限循環(huán)之內(nèi)被盈,這里取出消息析孽,如果為 null 則退出循環(huán):
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
注意當(dāng) MessageQueue 沒(méi)有消息時(shí),是不會(huì)返回 null 的只怎,只會(huì)一直循環(huán)等待消息袜瞬。只有當(dāng)調(diào)用 quit 方法退出時(shí),才會(huì)返回 null身堡。
public void quit() {
mQueue.quit(false);
}
接下來(lái)會(huì)對(duì)消息進(jìn)行處理:
msg.target.dispatchMessage(msg);
msg.target 就是發(fā)送該消息的 Handler邓尤,這里就會(huì)將線程切換到該 Handler 所在的線程去處理該 msg,這樣就完成了線程的切換啦~
Handler 的工作原理
在概述中我們已經(jīng)講過(guò)了,Handler.post 方法會(huì)將消息插入到 Looper 中的消息隊(duì)列汞扎,開啟循環(huán)后又會(huì)將該消息轉(zhuǎn)發(fā)到 Handler 所在線程進(jìn)行處理季稳,那么我們就來(lái)看一下 dispatchMessage 方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback 是 post 方法中傳遞進(jìn)去的 Runnable 參數(shù),如果不為空澈魄,則:
private static void handleCallback(Message message) {
message.callback.run();
}
如果為空景鼠,則判斷 Handler 的 Callback 是否為空,這個(gè) Callback 的定義如下:
public interface Callback {
public boolean handleMessage(Message msg);
}
我們可以用如下這種方式來(lái)創(chuàng)建一個(gè) Handler:
public Handler(Callback callback) {
this(callback, false);
}
實(shí)際上是與我們重寫 Handler.handleMessage 方法差不多的痹扇,只是另一種使用 Handler 的方式而已铛漓。
結(jié)語(yǔ)
那么以上就是,Android 的消息處理機(jī)制了鲫构,這一部分的代碼比較簡(jiǎn)單易懂浓恶,所以推薦大家都去看看。