上次文章談到ThreadLocal的原理。今天接著談談Android的消息處理機制。主要使用了四個類,Looper,Message,MessageQueue,Handler。先簡單介紹一個這個四個類的作用,再從源碼角度去分析。
- Looper
從名字可以猜測,他是不斷循環(huán)。思考一個問題,在我們new Thread創(chuàng)建一個線程并start之后,在執(zhí)行完中間的Runnable體之后,線程便結束他的工作,等待被回收。為什么Android主線程能一直運行,并且等待著處理UI事件和其他消息。我們猜測由于主線程被某種機制阻塞,使得主線程不能退出,一直在執(zhí)行狀態(tài)。而Looper就提供了這種作用。 - Message
被傳遞的消息體 - MessageQueue
存儲消息的隊列 - Handler
處理消息的處理器
Looper
有幾個主要的變量,sThreadLocal,sMainLooper,mQueue,mThread
prepare()方法往sThreadLocal這個類靜態(tài)變量里面放置了一個Looper對象,在set之前先判斷是否有值,如果有則拋出異常。從ThreadLocal的學習我們知道,ThreadLocal的set()和get()方法會區(qū)分線程,也就是說線程和Looper對象會一一對應。
Looper的構造函數(shù)需要一個quitAllowed參數(shù),在構造函數(shù)里,將該參數(shù)傳給了MessageQueue,并實例化該對象賦值給mQueue變量。mThread變量表示的是,實例化Looper時,即調用Looper.prepare()方法時所在的線程。
同樣prepareMainLooper()則是做了更多一步操作,設置
sMainLooper,myLooper()是從sThreadLocal里面取值。為什么調用myLooper()得到sMainLooper呢。因為prepareMainLooper()方法是系統(tǒng)調用的,系統(tǒng)會在應用啟動時調用設置sMainLooper這個靜態(tài)變量。
Looper還有兩個重要的方法,loop()以及quit()。
- loop()方法會取到當前線程對應的Looper對象,然后用for()循環(huán)不斷從Looper對象的mQueue里面取出Message對象,dispatchMessage這些對象。在msg為空時,跳出循環(huán)。結束loop方法。
- quit()方法則是調用mQueue的quit方法。
Message
幾個常用的public變量:what,arg1,arg2,obj
replyTo 和 sendingUid 是Messager使用時需要用到,暫時不需要關注。
data的作用和obj類似,區(qū)別在于obj在跨進程通信時,只能存放系統(tǒng)的Parcelable對象,其他類型的數(shù)據(jù)可以通過data傳輸。
target是一個Handler對象,在整個消息機制中需要使用到,后續(xù)再看。
Message對象存儲的信息可以是存儲在arg1,arg2,obj或是data中,也可以是一個存儲在callback的Runnable對象。next,指向Message對象的引用,MessageQueue中會使用到。
-
flags 消息的int型標志位,最后一位是否使用,倒數(shù)第二位是否為同步消息
image.png
Message提供了一系列簽名不同的obtain方法,其他需要傳參的方法都是在調用obtain()之后,對返回Message對象設置成員變量。obtain()方法會判斷sPool變量是否為空,如果為空就返回一個新的Message對象。如果不為空則返回sPool指向的Message對象,將該對象使用標志flags置為未使用,將next置為null(無法通過獲取到的message對象訪問下一個可使用的message),sPoolSize--,并且將sPool指向message.next。
recycle()方法用來回收被使用過的Message對象,在回收前會先檢查當前對象是否在使用,然后清除message所有字段,將使用標志置為被使用中,放入消息池中。
總結來說,obtain()是從message鏈表取出可被使用的message對象,這個鏈表是通過Message.next字段實現(xiàn)的,通過recycle()回收對象,obtain()再次使用,避免了新對象的生成。通過Message的sPool靜態(tài)字段,維護這個鏈表。
MessageQueue
MessageQueue是一個按時間排序的消息隊列。嚴格來說他并不是消息隊列,插入消息會按照時間排序插入而不是從隊尾插入,取出消息在設置同步障礙時,也不是從隊頭取出。
MessageQueue構造器使用了quitAllowed參數(shù),該參數(shù)只在quit()方法里面用到。從拋出的異常信息我們知道,主線程對應的MessageQueue對象不允許調用quit方法。只有主線程對應的MessageQueue傳遞了false,其他線程傳遞的應該都是true。MessageQueue對象的創(chuàng)建依賴于native的實現(xiàn)。在創(chuàng)建時候獲取了一個mPtr指針,在quit的時候,用該指針去釋放相關的資源。
Looper里面循環(huán)調用queue.next()獲取Message對象,分發(fā)Message。下面是MessageQueue的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;
}
}
...
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
1.nativePollOnce的作用是阻塞nextPollTimeoutMillis毫秒的時間。
2.在msg.target為null時,返回第一個達到when的異步消息。msg.target一般不為null,只有通過postSyncBarrier插入同步障礙Message時,target才會為null,此時不處理同步消息,只處理異步消息。
3.在msg.target不為null時,取出第一個達到when的消息。
4.在mMessages里取不到新消息時,nextPollTimeoutMillis為-1,nativePollOnce會一直阻塞。此方法造成了looper里queue.next()的阻塞。直到有新消息到來,調用nativeWake(mPtr)。
5.在調用Looper.quit()時,mQuitting會導致next里面循環(huán)退出。
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;
}
插入消息時,會驗證target和isInUse,以及mQuitting。
將消息標志flag置為InUse。在Message.recycle()和MessageQueue.enqueueMessage(Message msg, long when)時,都會判斷該字段。
在鏈表為空,或when=0,或when小于鏈表里message第一個when,將新message插入頭部。喚起由nativePollOnce造成的阻塞。
在鏈表不為空。(當前被阻塞,上一條是同步障礙消息,當前是異步消息)->喚起阻塞。根據(jù)when插入鏈表,越大的時間,插入到越后面。整條鏈表是按時間排序的
hasMessages(...)方法根據(jù)里面的參數(shù),判斷鏈表里面是否有對應消息。這個消息包括兩種,正常的消息以及runnable消息。
removeMessages移除對應的某一種消息
removeCallbacksAndMessages則是移除復合條件的所有消息
removeAllMessagesLocked移除所有消息
removeAllFutureMessagesLocked移除當前時間的之后的所有消息。
Handler
handler是message.target所對應的對象。在Looper.loop()中,在通過MessageQueue.next()取到msg之后,調用msg.target.dispatchMessage(msg)。我們看看Handler的相關方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
...
private static void handleCallback(Message message) {
message.callback.run();
}
...
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
1.Handler的構造器參數(shù)Callback會賦值給mCallback。根據(jù)當前線程獲取mLooper,以及mLooper里面的MessageQueue。如果沒有Looper則會報錯,這也就是常見的在子線程new Handler()會報錯,因為子線程默認沒有Looper。
2.在dispatchMessage時,先判斷當前message是不是runnable消息。如果是,則調用message.callback.run()執(zhí)行;如果不是,先嘗試使用mCallback.handleMessage()處理消息,如果mCallback為空,或者mCallback.handleMessage()返回false,調用Handler.handleMessage(Message msg)方法。該方法是空方法,handler子類自行實現(xiàn)。
3.Handler的obtainMessage系列方法直接調用Message的obtain的對應方法。post系列方法,是將runnable對象包裝成Message對象,調用MessageQueue.enqueueMessage()方法將消息放入待處理隊列中;
send系列方法是將Message對象調用MessageQueue.enqueueMessage()方法放入待處理隊列中。hasMessages和removeMessages方法是直接調用MessageQueue對應的方法。
4.Handler里面有個mAsynchronous成員變量,這個變量只有在調用Handler.createAsync靜態(tài)方法創(chuàng)建新Handler時候,賦值為true,其他時候都為false。該值為true時,在enqueueMessage的時候,會將Message對象設置成異步消息,也就是說,異步的Handler發(fā)送的消息,都是異步消息。
Android主線程啟動
#ActivityThread類(挑選出部分代碼)
public final class ActivityThread extends ClientTransactionHandler {
final Looper mLooper = Looper.myLooper();
final H mH = new H();
final Handler getHandler() {
return mH;
}
class H extends Handler {
public static final int BIND_APPLICATION = 110;
...
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
case BIND_APPLICATION: return "BIND_APPLICATION";
...
}
}
return Integer.toString(code);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
1.ActivityThread.main()方法是android應用主線程的入口,該方法在ZygoteInit里被調用。
2.先調用Looper.prepareMainLooper(),創(chuàng)建一個和主線程對應的Looper。
3.創(chuàng)建ActivityThread實例,啟動相關的類。ActivityThread.mH 是一個繼承自Handler子類對象,從handleMessage方法,看到了“serviceCreate”,“serviceBind”等方法。這個類是用來處理來自系統(tǒng)的一些消息事件。
4.Looper.loop()循環(huán)從MessageQueue里取出消息,處理消息,阻塞主線程。
總結
1.Looper通過ThreadLocal機制,保證該對象的實例和線程一一對應。在一個新開啟的線程里,通過Looper.prepare(),創(chuàng)建實例。在實例化時,MessageQueue也被實例化出來,作為mQueue成員變量保存在Looper對象里。Looper,MessageQueue,Thread三者一一對應。
2.Looper.loop()開啟,Looper所在線程將一直從MessageQueue里面取出Message,調用Message.target對應的Handler對象去處理消息。
3.Handler的創(chuàng)建默認取當前線程的Looper,也可以傳入一個已有的Looper。Handler對象可以往MessageQueue里插入消息,等到了處理時間,該Handler對象便會去處理消息。Handler對象可以在任意線程post或sendMessage,最后會在Looper所在的線程處理消息。