Android的消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,Handler的運(yùn)行需要底層的MessageQueue和Looper的支撐。Android規(guī)定訪問(wèn)UI只能在主線程進(jìn)行,如果在子線程中訪問(wèn)UI,程序會(huì)拋出異常,Handler的主要作用就是將一個(gè)任務(wù)切換到某個(gè)指定線程中去執(zhí)行。如在子線程獲取網(wǎng)絡(luò)數(shù)據(jù),再更新頁(yè)面UI就需要用到handler機(jī)制。
Handler主要涉及到四個(gè)類(lèi):Handler,Message,MessageQueue和Looper
MessageQueue
MessageQueue就是消息隊(duì)列,但是其內(nèi)部實(shí)現(xiàn)其實(shí)并不是隊(duì)列,而是通過(guò)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表。MessageQueue主要包含插入和讀取兩個(gè)操作,讀取操作本身又伴隨這刪除操作。插入對(duì)應(yīng)的方法是enqueueMessage,讀取對(duì)應(yīng)的方法是next。enqueueMessage就是往消息隊(duì)列中插入一條消息,next是從消息隊(duì)列中讀取一條消息并移除它。
enqueueMessage源碼如下:
boolean enqueueMessage(Message msg, long when) {
//msg.target就是處理消息的handler
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;
}
enqueueMessage方法返回一個(gè)布爾值,消息成功插入消息隊(duì)列返回true。方法體內(nèi)先判斷msg.target是不是為空,msg.target其實(shí)就是處理消息的handler,handler為空自然就處理不了消息。再判斷消息是不是已經(jīng)被處理了,從這個(gè)判斷可以得出消息不能被重復(fù)處理。接下來(lái)的操作就是單鏈表的插入操作,就不再贅述。如果線程是當(dāng)前是堵塞狀態(tài),最后還會(huì)喚起線程。
next方法源碼如下:
Message next() {
......
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞方法,主要是通過(guò)native層的epoll監(jiān)聽(tīng)文件描述符的寫(xiě)入事件來(lái)實(shí)現(xiàn)的。
//nextPollTimeoutMillis=-1,一直阻塞不會(huì)超時(shí)。
//nextPollTimeoutMillis=0,不會(huì)阻塞,立即返回。
//nextPollTimeoutMillis>0,最長(zhǎng)阻塞nextPollTimeoutMillis毫秒(超時(shí)),如果期間有程序喚醒會(huì)立即返回。
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.
//msg.target == null表示此消息為消息屏障(通過(guò)postSyncBarrier方法發(fā)送來(lái)的)
//如果發(fā)現(xiàn)了一個(gè)消息屏障,會(huì)循環(huán)找出第一個(gè)異步消息(如果有異步消息的話(huà)),所有同步消息都將忽略(平常發(fā)送的一般都是同步消息)
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.
//如果消息還沒(méi)有到時(shí)間,會(huì)設(shè)置一個(gè)阻塞時(shí)間
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.
//沒(méi)有消息,一直阻塞
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//消息隊(duì)列被標(biāo)記為退出狀態(tài),返回null
if (mQuitting) {
dispose();
return null;
}
......
}
......
}
}
next方法是一個(gè)無(wú)限循環(huán),如果消息隊(duì)列中沒(méi)有消息,next方法就會(huì)一直阻塞。有新消息時(shí),將新消息返回并移除這條消息。當(dāng)消息隊(duì)列被標(biāo)記為退出狀態(tài)時(shí),返回null,這里先賣(mài)一個(gè)關(guān)子,這個(gè)null會(huì)在文章后面講到。
Looper
Looper在Android消息機(jī)制扮演消息循環(huán)的角色,會(huì)不停的去MessageQueue中查看是否有新消息,如果有就立即處理,沒(méi)有的話(huà)就會(huì)一直堵塞在那邊。
Looper的構(gòu)造函數(shù):
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
構(gòu)造函數(shù)中會(huì)新建一個(gè)MessageQueue,然后將當(dāng)前的線程對(duì)象保存起來(lái)。
大家都知道創(chuàng)建Handler之前必須要現(xiàn)在線程中創(chuàng)建一個(gè)Looper對(duì)象,不然會(huì)報(bào)錯(cuò)。創(chuàng)建Looper對(duì)象也很簡(jiǎn)單,直接調(diào)用Looper.prepare()就可以了,創(chuàng)建Looper對(duì)象之后還不能實(shí)現(xiàn)消息循環(huán),必須再調(diào)用loop()方法來(lái)開(kāi)啟消息循環(huán)。讀到這里可能會(huì)有困惑,為什么在主線程創(chuàng)建Handler不需要我們自己創(chuàng)建Looper對(duì)象?答案就在ActivityThread里:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
從源碼中可以看到在主線程啟動(dòng)時(shí)就已經(jīng)調(diào)用了Looper.prepareMainLooper()方法,該方法是Looper提供給主線程使用的,接下來(lái)又調(diào)用了 Looper.loop()開(kāi)啟消息循環(huán)。這就是為什么我們?cè)谥骶€程創(chuàng)建Handler時(shí)不需要的自己創(chuàng)建Looper對(duì)象的原因。
再回過(guò)頭來(lái)開(kāi)子線程創(chuàng)建Handler的流程:
thread {
Looper.prepare()
val handler = Handler(object : Handler.Callback {
override fun handleMessage(msg: Message): Boolean {
doSomething()
}
})
Looper.loop()
}
首先創(chuàng)建一個(gè)Looper對(duì)象,接著創(chuàng)建Handler,然后再開(kāi)啟循環(huán)。(如上為了方便用kotlin代碼進(jìn)行舉例)
Looper提供了創(chuàng)建對(duì)象的方法之外還提供了quit和quitSafely方法,兩個(gè)方法的唯一區(qū)別就是quit會(huì)立即退出,quitSafely會(huì)等消息隊(duì)列中所有的消息處理完成之后再退出。退出之后Handler的send方法會(huì)返回false。不建議在主線程中調(diào)取quit方法,因?yàn)闀?huì)立即結(jié)束主線程。當(dāng)子線程中,應(yīng)當(dāng)在處理完所有消息之后調(diào)取quit方法來(lái)結(jié)束當(dāng)前線程。
Looper中最重要的方法就是loop方法,源碼如下:
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
loop方法也比較好理解,他也是一個(gè)無(wú)限循環(huán)的方法。for循環(huán)中會(huì)調(diào)取MessageQueue的next方法,在之前說(shuō)過(guò)next是一個(gè)阻塞方法,所以當(dāng)消息隊(duì)列中沒(méi)有消息的時(shí)候,會(huì)一直阻塞在這里,唯一跳出循環(huán)的方式就是next方法返回了null,這里就是解答了之前在講MessageQueue的時(shí)候?yàn)槭裁匆祷豱ull。當(dāng)Looper調(diào)quit方法的時(shí)候,Looper就會(huì)調(diào)用MessageQueue的quit方法,然后MessageQueue的next方法就會(huì)返回null,這樣loop方法才能跳出無(wú)限循環(huán)。當(dāng)loop拿到了消息之后就會(huì)調(diào)用msg.target.dispatchMessage(msg)「msg.target前面講到過(guò)就是發(fā)送這條消息的Handler」將消息交給Handler來(lái)處理了。這里需要注意的是Handler的dispatchMessage是在創(chuàng)建Handler時(shí)所使用的Looper中執(zhí)行的,這就就成功的切換到指定線程中去執(zhí)行了。
Handler
Handler的作用主要是消息發(fā)送以及接收。消息發(fā)送可以通過(guò)一系列的post方法和一系列的send方法,其實(shí)post方法到最后都是通過(guò)send方法來(lái)進(jìn)行發(fā)送。
public boolean sendMessageAtTime(@NonNull 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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
查看源碼發(fā)現(xiàn)到最后都是通過(guò)MessageQueue的enqueueMessage將消息插入到消息隊(duì)列中。之后Looper就會(huì)調(diào)用MessageQueue的next方法拿到消息,然后就進(jìn)入了下一個(gè)Handler處理消息的階段,調(diào)用Handler的dispatchMessage方法。源碼如下:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
1.先判斷msg的callback是不是為null,這個(gè)callback對(duì)象其實(shí)就是handler發(fā)送消息時(shí)調(diào)用post方法所傳遞的Runnable參數(shù),不為空就調(diào)用handleCallback方法處理。
private static void handleCallback(Message message) {
message.callback.run();
}
handleCallback方法也很簡(jiǎn)單,直接執(zhí)行Runnable的run方法。
2.其次檢查mCallback是否為null,部位null調(diào)用mCallback的handleMessage方法,查看源碼mCallBack其實(shí)是一個(gè)CallBack接口,CallBack具體如下:
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
mCallBack通過(guò)構(gòu)造函數(shù)傳入,當(dāng)你不想創(chuàng)建繼承自Handler的派生類(lèi)時(shí),就可以通過(guò)該構(gòu)造方法直接傳入一個(gè)Callback參數(shù)來(lái)實(shí)現(xiàn)消息處理。
val handler = Handler(object :Handler.Callback{
override fun handleMessage(msg: Message): Boolean {
...
})
3.最后調(diào)用Handler的handleMessage來(lái)處理消息
處理消息的流程圖大致如下:

額外注意點(diǎn):
- 消息機(jī)制里需要頻繁創(chuàng)建消息對(duì)象(Message),因此消息對(duì)象需要使用享元模式來(lái)緩存,以避免重復(fù)分配 & 回收內(nèi)存。具體來(lái)說(shuō),Message 使用的是有容量限制的、無(wú)頭節(jié)點(diǎn)的單鏈表的對(duì)象池,創(chuàng)建Message的時(shí)候最好用Handler的obtainMessage來(lái)進(jìn)行創(chuàng)建,盡量避免使用new Message()來(lái)創(chuàng)建造成內(nèi)存抖動(dòng)
2.在子線程中創(chuàng)建Handler時(shí)一定要先創(chuàng)建Looper對(duì)象,然后調(diào)用loop方法開(kāi)啟循環(huán)
3.盡量使用弱持有來(lái)創(chuàng)建Handler對(duì)象
思考:
1.一個(gè)線程可以創(chuàng)建幾個(gè)Looper?
2.MessageQueue是否線程安全?如果是 怎么保證?
3.Looper在主線程中死循環(huán),為啥不會(huì)ANR?
4.Handler會(huì)造成內(nèi)存泄漏嗎?為什么?怎么解決?