1、概述
相信大家對Handler的使用再熟悉不過了,Handler也經(jīng)常被我們應(yīng)用于線程間通信。下面看一段很經(jīng)典的代碼:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
當(dāng)我們在主線程或者在其它線程獲取到LooperThread線程中的mHandler實(shí)例并調(diào)用mHandler.sendMessage()時(shí),消息會(huì)傳遞到LooperThread線程中并在handleMessage方法中執(zhí)行對消息的特定處理。不知道大家有沒有想過為什么在一個(gè)線程發(fā)送消息另一個(gè)目標(biāo)線程能夠正確接收并做相應(yīng)處理?這篇文章就帶領(lǐng)大家從源碼的角度一步步理解Android的消息機(jī)制。Android的事件處理是基于消息循環(huán)的,Android的消息機(jī)制離不開Looper、MessageQueue、Handler,其實(shí)理解Android的消息機(jī)制就是理解以上三者的作用與聯(lián)系。
2、Looper
我們先來看看官方文檔對Looper的解釋
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler class.
從中我們可以知道,Looper是一個(gè)線程的消息循環(huán)器,線程默認(rèn)是沒有消息循環(huán)器的。我們可以通過prepare方法為一個(gè)線程創(chuàng)建消息循環(huán)器,然后通過調(diào)用loop方法處理消息直至循環(huán)終止。我們看看調(diào)用prepare方法后都發(fā)生了什么:
public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
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));
}
在prepare方法中首先會(huì)通過調(diào)用ThreadLocal的get方法判斷當(dāng)前線程的消息循環(huán)器Looper是否為空,為空則通過Looper構(gòu)造器創(chuàng)建實(shí)例,否則拋出異常。從異常信息我們可以知道一個(gè)線程只有一個(gè)Looper。在這里ThreadLocal的作用是關(guān)聯(lián)線程和Looper,一個(gè)線程對應(yīng)一個(gè)Looper,即調(diào)用prepare方法的線程會(huì)和prepare方法創(chuàng)建的Looper實(shí)例關(guān)聯(lián),且一個(gè)線程和唯一一個(gè)Looper關(guān)聯(lián),當(dāng)?shù)诙握{(diào)用prepare方法時(shí)程序會(huì)拋出異常。接下來我們看看通過Looper構(gòu)造方法實(shí)例化對象時(shí)發(fā)生了什么:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
從源碼我們能夠看到實(shí)例化Looper時(shí),會(huì)初始化消息隊(duì)列MessageQueue(MessageQueue后續(xù)會(huì)詳解)以及記錄當(dāng)前Looper關(guān)聯(lián)的線程。MessageQueue作為Looper的成員變量在此時(shí)也與Looper建立了關(guān)聯(lián)。接下來我們看看Looper的另外一個(gè)方法loop:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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();
}
}
從以上源碼我們不難理解,首先通過myLooper()方法獲取當(dāng)前線程的Looper實(shí)例,如果為空則拋出必須先調(diào)用prepare方法為當(dāng)前線程關(guān)聯(lián)Looper的異常信息。接下來進(jìn)入到loop方法中最重要的部分是一個(gè)for(;;)死循環(huán)。在循環(huán)中Looper關(guān)聯(lián)的消息隊(duì)列MessageQueue會(huì)通過調(diào)用next方法獲取消息隊(duì)列中的消息Message,并且循環(huán)會(huì)把消息分發(fā)給相應(yīng)的目標(biāo)對象。 msg.target.dispatchMessage(msg);這行代碼即是對消息的分發(fā)。其中target是Handler實(shí)例,即調(diào)用sendMessage方法發(fā)送的消息所關(guān)聯(lián)的Handler。Handler調(diào)用dispatchMessage分發(fā)消息最總會(huì)調(diào)用handleMessage方法這樣就進(jìn)入到了我們對消息的處理流程(后文會(huì)對Handler進(jìn)行詳解,這里提到只是為了便于大家理解)。我們看到Message msg = queue.next(); // might block這行源碼做了注釋,意思是next方法的調(diào)用可能會(huì)發(fā)生阻塞。也就是說當(dāng)消息隊(duì)列中有消息時(shí),next方法會(huì)返回消息對象否則當(dāng)前線程會(huì)一直阻塞直至隊(duì)列中有新消息把當(dāng)前線程喚醒。不知道大家注意到了loop方法的注釋沒有,Be sure to call * {@link #quit()} to end the loop.我們要調(diào)用quit方法結(jié)束循環(huán)。調(diào)用quit方法最終會(huì)使得MessageQueue next方法返回null,當(dāng)為空時(shí)會(huì)跳出for死循環(huán),這樣線程最終才可能結(jié)束生命。如果線程在處理完自己的任務(wù)后不調(diào)用quit方法,線程將一直阻塞或被新的無用消息喚醒而最終無法終止,這無疑是對資源的浪費(fèi)。
至此,Looper的源碼分析完了,當(dāng)然有一些方法并沒有分析(請大家自行閱讀),現(xiàn)在我們對Looper做一個(gè)總結(jié):一個(gè)線程唯一對應(yīng)一個(gè)Looper,一個(gè)Looper唯一關(guān)聯(lián)一個(gè)消息隊(duì)列MessageQueue。Looper不斷從消息隊(duì)列中取出消息并分發(fā)給目標(biāo)對象。還有要注意的是線程處理完相應(yīng)任務(wù)后要調(diào)用quit方法結(jié)束循環(huán),否則會(huì)造成不必要的資源浪費(fèi)。
2、MessageQueue
接下來我們繼續(xù)分析MessageQueue,首先我們還是看官方文檔的解釋
/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
官方文檔解釋的很清楚,MessageQueue持有待分發(fā)的消息列表。我們通常不會(huì)直接操作MessageQueue插入消息而是通過Handler。我們可以通過Looper類的方法myQueue獲得當(dāng)前線程的MessageQueue。MessageQueue最重要的兩個(gè)作用是消息入隊(duì)和消息出隊(duì)。首先我們來看看入隊(duì)方法enqueueMessage的源碼
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;
}
我們能夠看到前面幾行代碼對消息狀態(tài)進(jìn)行了判斷,包括如果消息分發(fā)的目標(biāo)對象為空或者消息已經(jīng)被使用將拋出異常,以及如果終止了消息循環(huán),消息將不會(huì)插入到隊(duì)列中并且返回false。接下來是消息入隊(duì)的核心代碼,可以看到是典型的鏈表結(jié)構(gòu):如果表頭為空則創(chuàng)建表頭,否則將數(shù)據(jù)插入到表的末尾。通過分析代碼我們也能夠知道,雖然MessageQueue字面意思是消息隊(duì)列,但它真正的內(nèi)部數(shù)據(jù)結(jié)構(gòu)并不是隊(duì)列而是普通鏈表。接下來我們繼續(xù)看出隊(duì)方法next的源碼,我們截取其中比較重要的一部分
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;
}
從代碼中我們不難看出消息出隊(duì)其實(shí)就是鏈表表頭的向下移動(dòng),每次從消息隊(duì)列中取出一個(gè)消息,就返回表頭指向的Message實(shí)例,并且表頭向下移動(dòng)一位。上文我們提到了用Looper 的quit方法終止消息循環(huán),其實(shí)最終調(diào)用的是MessageQueue的quit方法。quit方法有個(gè)safe參數(shù),表示是否安全終止循環(huán)。所謂安全是當(dāng)調(diào)用quit方法后消息隊(duì)列中無法插入新的消息,但是循環(huán)可能不會(huì)立即終止,直至消息隊(duì)列中待分發(fā)的消息(如果有)分發(fā)完畢。不安全的終止與之相反,消息循環(huán)會(huì)立即終止,新的消息也無法插入。至此,MessageQueue的主要方法分析完畢了,其它的一些方法如removeMessage、quit等請大家自行閱讀分析,下面我們隊(duì)MessageQueue做一個(gè)總結(jié):MessagQueue的作用主要是存儲(chǔ)消息,并且對外提供一些接口對消息操作如插入消息、取出消息、移除消息等。