Android版本: 基於API源碼26,Android版本8.0。java
本片文章的目的在於全面的瞭解Handler。它是如何傳遞消息的?是如何阻塞和喚醒線程的(僅限於Java層面)?MessageQueue究竟是怎麼存儲和取出Message?延遲消息是怎麼被髮送的?android
Handler是一套消息傳遞的機制。設計用來讓工做線程跟主線程之間進行消息傳遞。同時也解決了多線程同時更新UI的問題。Android系統設定子線程是不能更新UI的,當子線程必定要作更新UI的操做時,可使用Handler將消息從子線程帶到主線程,並能保證消息的同步性。git
在主線程中使用:github
//實例化Handler對象。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
//建立併發送消息
Message obtain = Message.obtain();
obtain.what = 100;
handler.sendMessage(obtain);
複製代碼
在子線程中使用,一旦使用該子線程是不會自動結束的,除非手動結束:json
//爲子線程中建立Looper對象。
Looper.prepare();
//實例化Handler對象。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
//建立併發送消息。
Message obtain = Message.obtain();
obtain.what = 100;
handler.sendMessage(obtain);
//開啓消息循環。
Looper.loop();
複製代碼
若是先調用Looper.prepare()、Looper.loop()再handler.sendMessage(obtain),那麼消息會成功的發送成功嗎?api
Handle的消息傳遞過程當中涉及到了幾個重要的類:Handler、Message、Looper、MessageQueue。緩存
Handler安全
官方的解釋:Handler容許您發送和處理Message
和MessageQueue
有關聯的可運行對象Runnable。每一個Handler實例都與一個線程和該線程的消息隊列關聯,在建立的時候就會直接關聯起來。Handler將Message和Runnable傳遞到消息隊列中,並在它們從消息隊列中出來時執行它們。多線程
Handler被用在兩個主要的地方:讓Message和Runnable定時執行;在非UI線程中將Message或者Runnable發送到主線程。併發
當應用程序建立進程時,其主線程專用於運行消息隊列,該隊列負責管理頂級應用程序對象(activities、broadcast receivers等)及其建立的任何窗口。
也就是Handler是用來專門發送和處理消息的。發送是隻發送到對應的消息隊列就行了。
Message:
官方解釋:定義包含描述和可發送到{@link handler}的任意數據對象的消息。此對象包含兩個額外的int字段和一個extra對象字段,在許多狀況下容許您不進行分配。
也就是Message是Handler傳遞的元數據,其內部能夠包含不少信息:what、arg一、arg2,、obj等。Handler發送的也是Message對象,處理的也是Message對象。因此Message確定是序列化的,繼承自Parcelable。內部維護了一個緩存池,避免建立多餘的消息對象,緩存池的設計是Android系統源碼一向的做風,在ViewRootImpl處理點擊消息的時候,也有相似設計。該緩存池的大小爲50,超過該緩存就會直接建立Messsage對象。
Looper:
官方解釋:Looper類用於爲線程運行消息循環。默認狀況下,線程沒有與之關聯的消息循環;若要建立一個消息循環,請在要運行該循環的線程中調用 prepare()方法,而後調用loop()使其處理消息,直到循環中止。
也就是Handler只是將Message數據放到了MessageQunue中,Looper纔是從MessageQunue中取出消息,並將消息發送到正確的Handle中去。一個線程中存在一個Looper,Looper中維護着MessageQueue。Looper.prepare()方法是建立Looper跟MessageQueue,再發送消息以後調用Looper.loop()方法,開啓消息循環,也就是不停的從消息隊列中那出消息處理。
MessageQueue:
該機制中核心的存在。主要是用來操做Message的,實際上內部並無一個集合或者隊列去存儲Message,而是由Message組成的單鏈表結構。在Message的內部有個next屬性,屬性聲明爲Message對象,也就是Message自身就可組成一個隊列。MessageQunue的目的就是增刪對比Message。好比:第一個Message對象來了(m1),那麼它的next屬性是空的,而後又來了一個Message(m2)。發現m1還沒執行完畢,那麼就將m2的對象賦值給m1的next屬性,當m1執行完畢以後,取出本身的next屬性,若是不爲null,那就接着執行next屬性表示的Message,也就是m2。
當了解完每一個類的職責以後,發送的消息的流程就會清晰的不少。
通常的,一個Thread應該只有一個Looper和MessageQueue。能夠有多個Handler對象。當你在主線程中建立Handler的時候,你是不須要調用Looper.prepare()的,由於主線程已經建立過了。建立Looper的線程決定了消息最終是在那個線程中被處理的。你在子線程中使用Handler對象發送消息,若是你的Looper是主線程中建立的,那麼處理消息的仍是在主線程中,你發送消息的線程環境是可有可無的。
Handler建立源碼解析:
Handler類是個普通的Java類,能夠被繼承。建立方式爲:
//通常經過new的方法去建立。
Handler handler = new Handler();
複製代碼
Handler有七個構造方法,三個是被隱藏掉的。被註解@hide註釋以後,經過api是訪問不到的:
//Handler.java
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/** * @hide */
public Handler(boolean async) {
this(null, async);
}
/** * @hide */
public Handler(Callback callback, boolean async) {
//。。。省略代碼
//獲取Looper對象。
mLooper = Looper.myLooper();
//。。。省略代碼
//獲取MessageQueue。
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
/** * @hide */
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製代碼
能夠看到構造參數中能夠傳入的類型:Callback接口,Looper對象,布爾值async。首先Callback接口也是處理消息的:
//Handler.java
public interface Callback {
/** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */
public boolean handleMessage(Message msg);
}
複製代碼
Handler類內部也有handleMessage()方法,使用的時候徹底能夠重寫該方法。Handler又提供該接口的目的也就是你能夠不用實現Handler類中的方法也能處理消息,這樣自由一點。
若是構造方法中你傳入了Looper對象,就不須要去建立Looper對象了。至於布爾值async則是決定當前發送的Message是不是異步的消息,異步消息能夠不受同步屏障的影響。在ViewRootImpl接收到繪製信號的時候,就會在UI Handler中發送一個同步屏障,這將阻止當前Handler處理全部的同步消息,而後讓該此異步消息先執行。Handler的建立過程很簡單,接下來看Message的建立過程。
Message建立源碼解析:
Message類是final的不能被繼承。實現了Parcelable接口。Message並無特殊的構造方法,因此直接就可new出來:
Message message = new Message();
複製代碼
另外系統推薦的獲取Message的方法爲:
Message obtain = Message.obtain();
複製代碼
obtain()方法有不少的重載方法,根據參數的不一樣構造不一樣的Message對象。該過程都是在Message內部實現的,對於調用着來講是透明的,下面來分析下基礎的obtain()方法:
//Message.java。
public static Message obtain() {
synchronized (sPoolSync) {
//判斷緩存池是否爲null。
if (sPool != null) {
Message m = sPool;
//取出一個Message。
sPool = m.next;
m.next = null;
//默認的flag爲0。
m.flags = 0; // clear in-use flag
//大小減一。
sPoolSize--;
return m;
}
}
//不然就new一個
return new Message();
}
複製代碼
sPool是一個Message對象,至關於緩存隊列的頭指針。Mesage有個next屬性,該屬性仍是Message對象。當調用該方法的時候,就從sPool中取出下一個Message也就是m.next,而後將m.next賦值給sPool,也就是重置隊列頭,而後返回以前的隊列頭m。這種是典型的單向鏈表結構,Android系統的源碼中不少地方都用到。而後在Message的reaycle()方法中,將使用過的Message存放到sPool表明的隊列中。
//Message.java。
public void recycle() {
if (isInUse()) {
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
//。。。省略代碼
synchronized (sPoolSync) {
//判斷緩存的數量是否小於規定值的
if (sPoolSize < MAX_POOL_SIZE) {
//將隊列頭指針指向下一個元素
next = sPool;
//隊列頭重置,這樣新的消息就變成了隊列的第一個元素,以前sPool表示的消息就變成該消息的next。
sPool = this;
sPoolSize++;
}
}
}
複製代碼
Message的建立很簡單,須要注意的就是這個緩存池的原理。
消息發送流程源碼解析:
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Message obtain = Message.obtain();
obtain.what = 100;
handler.sendMessage(obtain);
複製代碼
上述代碼便可發送一個消息數據。無論是sendMessage()仍是sendMessageDelayed()或者sendEmptyMessage()方法,最終都是調用的sendMessageAtTime()方法,sendEmpty***系列是Handler本身建立了一個空的Message,只是在使用着看起來是發送了一個空的消息同樣,這裏再也不分析:
//Handler.java。
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);
}
複製代碼
該方法中只是判斷了MessageQueue是否爲null。
//Handler.java。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//該消息的目標Handler爲當前發送消息的Handler。
msg.target = this;
//若是是異步消息的話就設置爲異步標記。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
對Message對象作了一些標記以後就調用了MessageQueue的enqueueMessage()方法,這兩個標記都是很重要的,一個是設置目標Handler,這樣消息就不會讓錯誤的Handler處理。另一個是代表消息是不是異步的,通常使用的時候都是同步消息,UI線程畢竟是線程不安全的:
//Message.java。
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
複製代碼
接着看queue.enqueueMessage()方法:
//MessageQueue.java。
boolean enqueueMessage(Message msg, long when) {
//若是消息沒有目標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) {
//當前的消息隊列已經結束了。mQuitting = true。
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;
//mMessages表示當前準備處理消息。
Message p = mMessages;
boolean needWake;
//1.當前要處理的消息爲null。2.須要立刻發送,延遲時間爲0。3.若是當前要處理的消息的延遲比正在處理的消息時間段。
//上面三個條件知足一個就執行。若是是第一條消息if確定會執行,若是來了第二條Message,可是第一條Message還沒執行完,也就是mMessages不爲null,消息的when還等於0,那麼也要直接執行,也就是添加到隊列的頭部。因此markInUse()方法仍是頗有必要的。
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 {
//當下一條消息來的時候發現前面還有消息沒執行完,而且當前的消息也不是立馬就執行的,或者等待的時間比正在執行的消息要長,那麼就執行到這邊。
//p.target == null表示當前的消息是一個同步屏障。
//needWake = true,說明當前的前一條消息是個同步屏障,而且當前的這條消息是個異步消息。
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;
}
//插入隊列中間。一般咱們沒必要喚醒事件隊列,除非隊列的開頭有一個屏障,而且消息是隊列中最先的異步消息。
//判斷是否喚醒線程。
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
複製代碼
上面的入對列操做很複雜:
通過上面的步驟,Handler將發送的Message放入到了消息隊列中。Handler類的職責已經結束了。下面來看Looper到消息隊列中取數據。
Looper源碼解析:
Looper類的註釋中說明了,在使用的時候要先調用Looper.prepare()方法,最後調用Looper.loop()方法。下面先看下prepare()方法:
//Looper.java。
//該方法是能夠在任何線程中調用。
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));
}
//該方法在ActivityThread建立的時候會被調用。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
複製代碼
prepare()系列方法就是建立一個Looper對象。採用prepareMainLooper()方法建立的Looper不會終止消息循環,其餘的均可以被終止掉,也就是quitAllowed = true/fasle的問題。Looper建立結束以後看下loop()方法:
//Looper.java。
public static void loop() {
final Looper me = myLooper();
//。。。省略日誌代碼
//Handler跟Looper用的是同一個隊列。
final MessageQueue queue = me.mQueue;
//確保此線程的標識是本地進程的標識,並跟蹤該標識令牌的實際內容。
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;
}
//。。。省略日誌代碼
try {
//msg.target就是Handler,而後分發消息。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
//。。。省略日誌代碼
}
//。。。省略日誌代碼
//消息處理結束以後回收消息。
msg.recycleUnchecked();
}
}
複製代碼
loop()方法要作的就是不停的從MessageQueue中獲取消息,因此就使用了For死循環。拿到消息以後,開始分發消息,最後將消息回收。重點在於Message的next()和Handler的dispatchMessage()以及最後Message的recycleUnchecked()方法了。先看next()方法:
//MessageQueue.java
Message next() {
//若是消息循環已經退出並被釋放,則返回此處。若是應用程序在退出後嘗試從新啓動不受支持的循環程序,則可能發生這種狀況。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//空閒時待處理的IdleHandler的數量。默認爲-1。
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//下次輪詢超時毫秒
int nextPollTimeoutMillis = 0;
//開啓死循環。要麼阻塞,要麼獲取到Message。
for (;;) {
//將當前線程中掛起的任何綁定器命令刷新到內核驅動程序。在執行可能會阻塞很長時間的操做以前調用,以確保已釋聽任何掛起的對象引用,以防止進程佔用對象的時間超過所需時間,這將很是有用。
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//該方法可能會被阻塞,nextPollTimeoutMillis = -1的時候,將無限期的阻塞。也就是說當該方法有返回值的時候,那就表明該過程不會被阻塞,也就是會執行下面的代碼邏輯,也就是將會至少取出一個消息。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 獲取系統時間。
final long now = SystemClock.uptimeMillis();
//該Message是表示將被執行的消息的前一個消息,有多是從消息隊列的中間取出一個消息執行,這樣須要將被取出消息的next拼接到被取出消息的上一個消息的next上,這樣隊列不會被斷開。
Message prevMsg = null;
//隊列的頭消息。每次取出一個消息的時候都會將mMessages指向新的消息。
Message msg = mMessages;
if (msg != null && msg.target == null) {
//執行到這裏說明當前的消息是一個同步屏障,也就是將阻止全部的同步消息。
//找出隊列中的第一個異步消息,沒有異步消息msg就是null。
do {
//找出消息的上一個消息。
prevMsg = msg;
//當前找出的消息。
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//這裏並非else語句,因此就是否是同步屏障就直接執行到這裏。
if (msg != null) {
if (now < msg.when) {
// 下一條消息還沒有準備好。設置一個超時,以便在它準備好時喚醒。也就是nextPollTimeoutMillis參數其實就是上面nativePollOnce()本地方法的參數,也就是阻塞的時長。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//走到這裏說明找到了符合要求的Message,準備返回了。
mBlocked = false;
if (prevMsg != null) {
//prevMsg不爲null表示當前的msg是一個異步消息,那麼其值表示msg消息的上一個消息。也就是msg是從隊列中間取出來的,那麼就須要把隊列拼接完整了。
prevMsg.next = msg.next;
} else {
//走到這裏說明msg是一個同步消息,也是從隊列的頭部獲取到的,那麼就重置mMessages爲msg的下一個消息。
mMessages = msg.next;
}
//清空該消息的next屬性,這樣該消息就是一個獨立的消息。在上一步中該消息的next屬性已經賦給了隊列中的其它Message。
msg.next = null;
//標記正在使用
msg.markInUse();
return msg;
}
} else {
// 沒有消息就爲-1.-1就表示該線程將無限期的阻塞。
nextPollTimeoutMillis = -1;
}
//處理完全部掛起的消息後,當即處理退出消息。
if (mQuitting) {
dispose();
return null;
}
//。。。省略了空閒時處理IdleHandler的邏輯。IdleHandler對象,IdleHandler是一個接口,也就是繼承了該接口的事物將會在MessageQueue空閒的時候去處理。GC機制就是採用的這個東西觸發的。
if (pendingIdleHandlerCount <= 0) {
//沒有東西能夠執行了,就開始等待工做。
mBlocked = true;
//continue繼續執行for循環,而後在執行nativePollOnce()方法,線程阻塞。
continue;
}
}
}
複製代碼
在Looper的loop()方法中第一個重要的就是MessageQueue的next()方法。該方法有如下幾點:
msg.target == null
,是的話就要拿出隊列中第一個異步消息,也就是Message的isAsynchronous()方法返回true。若是不是同步屏障,就繼續往下走。上述就是MessageQueue的next()方法。通過該方法後繼續看Looper的loop()方法:
//Looper.java。
public static void loop() {
//。。。省略代碼
for (;;) {
//。。。省略代碼
try {
//msg.target就是Handler,而後分發消息。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
//。。。省略日誌代碼
}
//。。。省略日誌代碼
//消息處理結束以後回收消息。
msg.recycleUnchecked();
}
}
複製代碼
這裏獲取到Message的target屬性,而後調用了dispatchMessage()方法。target屬性是在Handler的enqueueMessage()方法中賦值的:
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
也就是當前Handler對象。以後就是Handle的分發方法:
//Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
代碼一目瞭然。若是msg.callback不爲null,說明當前的消息是Runnable對象。不是的話,判斷mCallback是否爲null,也就是Handler的構造方法中的參數Callback,不爲null就調用Callback的handleMessage()方法。再接着就是調用自身的handleMessage()方法。到這Handle的源碼分析就結束了!重點難點全在MessageQueue類中。