我知道關於Handler的文章在網上已經快被寫爛了/笑着哭,但是還是想寫出來,我覺得只有自己親自寫過這些東西,纔會更深入的理解這些。
雖然以前也寫過Handler,但是當時剛剛接觸Handler的源碼,所以寫的像屎一樣。。。。(在簡書上寫的,不忍直視)。現在本人自我感覺關於Handler已經有個相對有一點自己見解了,所以今天會更加細緻的寫這篇博客。
好了不多說了,開始我們今天的正題:
這個東西如果直接從源碼講起來會很亂,所以今天我們先從我們接觸到的一些類和方法開始講。
Handler使用(簡述)
關於Handler的使用這裏就不多說了,相信在看這篇文章的時候大家已經把Handler用爛了。簡述一下調用的幾個方法:handler.postXXX(Runnable,。。。。);
還有handler.sendXXX(。。。。);這是我們使用Handler的兩類方法,大家應該都會用的,不用多說。
Handler的構造方法中出現的相關方法
我們使用Handler,首先要有一個Handler的對象,通常我們最簡單的方法,就是直接new ,然後重寫handlerMessage方法:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
當然也可以在參數裏面加上Callback回調來用(個人喜歡這種,因爲第一種warning是一坨黃色的,看着很煩-。+):
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
先進入Handler的第一個構造方法:
public Handler() {
this(null, false);
}
這裏面的兩個參數,我們在Android Studio中可以發現,第一個就是我們的Callback,第二個是說是否異步(我們不管他這個東西)。
點進this看一下:
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我省略掉了上面的一些代碼,只要看一下這一段就好:
這裏唯一一個比較陌生的就是Looper,其他的都能看懂。其實應該也不算陌生:因爲在學Handler時候,各個書上差不多都有一個簡述Handler的工作原理,裏面都有提到過Looper,這個Looper稱爲輪詢器,負責不停地輪詢,把我們的消息取出來,然後執行。這只是大概工作原理,其實如果真的喫透Handler機制會發現,真正的輪詢另有其人(下面會提到)。
既然他先出來的,那麼我們先看一下mLooper方法:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
這個方法返回了和當前線程關聯的looper對象。
先說一下這個sThreadLocal:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
這是在Looper中的一個靜態引用,這麼看來我們所有的Looper都要共享這個mThreadLocal了,我們可以把這個mThreadLocal理解爲一個大箱子,裏面放着很多的Looper對象。上面用到了ThreadLocal的get方法,我們看一下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
他返回值是當前線程的某個值,現在我們把泛型指定爲Looper ,所以他肯定是拿到了當前線程的Looper,這個很容易理解。接着我們簡單說一下下面出現的這幾個類:
- ThreadLocalMap
- Entry
首先通過getMap方法拿到map對象,我們看一下getMap方法:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
是返回了Thread中的一個屬性,這樣看來Thread中有ThreadLocalMap的引用。Thread和ThreadLocalMap可以關聯。
在上述代碼中我們注意到最後是從Entry e中拿到了value作爲返回值返回,這麼推理是Entry中存儲了我們的Looper。我們看getEntry方法:
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
table是ThreadLocalMap的一個屬性:
private Entry[] table;
我們通過table拿到Entry對象,然後返回這個e。(在一般不出意外的情況下)
現在我們跳回到上面的get方法中,目前我們已經拿到了e對象,如果不爲空會返回e存儲的值(value):
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
這是我們myLooper的方法。簡單總結一下:
我們調用了sThreadLocal的get方法,在get方法中首先獲取到當前線程(Thread.currentThread()),然後拿到對應線程的ThreadLocalMap對象(getMap),最後通過ThreadLocalMap拿到Entry對象(getEntry),返回e的value值。
我們在Handler的構造方法上耗時有點多了,開始掃尾:
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
跳回我們Handler構造方法:在獲取到looper對象之後,我們就是給Handler的屬性賦值,這裏又出現了一個新的類,mQueue對應的類——MessageQueue,關於這個類我們下面詳細講,現在我們只需要Handler和Looper保留了它的引用。在Handler構造方法中進行了相關屬性賦值。到現在我們Handler的構造方法可以過了。
handler.sendXXX(。。。。);
上面說到Handler發送消息有兩種方式,我這裏就那sendMessage方法舉例了(其實最好會發現,所有方法都一樣-。+):
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
這是我們點進了sendMessage方法,我們看到他是嵌套了一個方法,我們再點進去看一下:
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);
}
也是嵌套,但是這個嵌套有點東西:
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
注意這兩行代碼,在接下來的方法中,我們又放入和一個MessageQueue引用做參數,現在我們再點進這個方法看:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
好了,現在又有一個Message對象,是不是一個沒搞懂,又冒出來好幾個的感覺,大家淡定,先分析一下他的邏輯,只有這幾個類會在下面詳細介紹的:
我們將msg的target屬性設爲this,這個方法是在Handler中的,沒猜錯的話target應該是Handler類型的,點進去看一下:
/*package*/ Handler target;
果然如此哈哈!
最後返回的是MessageQueue的方法了,好我們就此打住。
總結handler.sendXXX():
經過層層方法嵌套,最後進入了MessageQueue的enqueueMessage方法。
接下來我們重點看一下剛纔提到的還有在Handler中幾個重點了類:
MessageMessageQueue
Looper
我們分別看一下:
Message
目前主要看這幾個屬性就好:
/*package*/ Handler target;
/*package*/ Runnable callback;
/*package*/ long when;
// sometimes we store linked lists of these things
/*package*/ Message next;
第一個是我們剛纔看到的target,我們在enqueueMessage方法中,將當前的Handler賦值給了msg的target。
callback:這個屬性在我們通過post方法發送消息的時候會用到,具體大家自己分析完post方法就知道了。when:這個是一個用作記錄時刻的屬性,官方解釋爲運行回調的絕對時間。
next:相信大家都知道鏈表是個什麼東西,這個next就是一個指向鏈表下一元素的指針。
其他的我們等會兒遇到了再說,現在先知道這三個屬性就好。
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()}.
*/
我們只看其中一句很重要的:Messages are not added directly to a MessageQueue:我們的Message不是直接儲存到MessageQueue,這是神魔意思?我查找了一下MessageQueue的所有屬性,顧名思義,如果MessageQueue是用來存儲Message的,那麼首先肯定有用一個集合相關的屬性,來存儲Message,可是我看完了所有的屬性,。。。。。。真的沒有!
但是我們發現了熟悉的身影:mMessage屬性,他是Message類。還記得我們上面提到過的Message.next屬性嗎?我們是不是可以大膽地猜測一下:MessageQueue中放置了一個Message鏈表的頭結點,然後通過Message元素自身添加和刪除,實現Message的鏈式存儲。(目前這個只是咱們的猜測)
剛纔在Handler.sendXXX()中,最後跳轉到的就是MessageQueue的enQueueMessage方法,我們來看一下這個方法(前方高能!):
MessageQueue.enqueueMessage
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
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.
//原來mMessages爲鏈表頭,現在讓msg變成了鏈表頭,原來的mMessagges引用的Message對象在msg的後面。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
......
/*
* msg不是新的頭,所以要按照時間順序排列鏈表,查找對應的位置*/
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;
}
......
}
return true;
}
爲了廣大的讀者,我已經刪除了一部分無用的代碼,但是還看着很多。我們逆着想一下:我們handler.sendXXX方法是把我的Message對象放入了隊列中,所以最後跳轉到的這個方法應該是一個存儲Message的過程,首先我們有着這樣的一個總體思路,然後接着看:
首先我們下面所有的代碼都寫入了同步鎖鎖住的代碼塊中,首先給傳入msg的when屬性賦值,新建了一個Message引用等於mMessages。我們的進行了一個簡單的if判斷,無論鏈表爲空,還是絕對時刻爲0,或者是新插入的msg時刻比頭結點的時刻小(簡單理解是在它之前),這三種情況只要滿足其中一種,就把新插入的msg設爲新的頭結點。這三行代碼就是指定新的頭結點,自己看去。。。
如果這三種情況都不是,那麼就要查找msg應該對應的位置,標準就是絕對時刻(when):通過遍歷隊列,直到找到對應位置,如果到最後還沒有對應的位置,說明他的絕對時刻最大,所以放到最後。
最後返回true。
這是我們enqueueMessage方法的過程。對應代碼上又備註。
MessageQueue還有一個很重要的方法:next()(至於用處一會兒回用到):
MessageQueue.next():
Message next() {
......
for (;;) {
......
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;
}
......
}
......
......
}
}
又刪除了百分之80的垃圾代碼-。+。剛纔我們說enqueueMessage方法是把Message放入,那麼next方法就是將Message取出的。我們來看一下:
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
我們先看這一段代碼:首先新建了prevMsg,一個新的Message引用,然msg指向頭結點。
接着把preMsg和msg移動到同步Message位置(因爲這段程序整個都在同步鎖中,異步的Message不受限制,這段話可以當做放屁-。+)。
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;
}
}
這段是將msg取出,然後將鏈表斷開位置重新連接。(詳細邏輯如上,大家自己看)
Looper
這個類是我們需要說的最後一個類了,感覺很累。。我們知道如果在子線程中使用Handler,我們還需要做額外的兩件事:Looper.prepare()和Looper.loop();這兩個方法有什麼作用呢?我們來看一下:
Looper.prepare()
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));
}
相信到這裏大家看的應該比較輕鬆了,因爲幾乎都是我們剛剛瞭解的方法:
首先進行判斷,在sThreadLocal中是否存在當前線程的looper對象,在初次創建時候,是沒有的。
然後創建一個新的Looper對象,放入sThreadLocal中。
我們看一下set方法:
Looper.set():
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
邏輯很清晰:
- 獲取當前線程對象。
- 獲取線程的ThreadLocalMap對象。
- set方法添加Looper對象。
ThreadLocalMap.set(。。。):
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
這個不怎麼重要,我們只需要知道最後我們把value添加給了Entry[] table就好。
Looper.loop():
public static void loop() {
final Looper me = myLooper();
......
final MessageQueue queue = me.mQueue;
......
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
......
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
}
loop方法是用來輪詢的的,在這裏我們也看到了一個for死循環,我們看一下方法邏輯:
- 獲取當前線程的looper對象。
- 進入死循環,不斷獲取當前線程的消息隊列中的Message對象。
- 分發:msg.target.dispatchMessage(msg);
handler.dispatchMessage(Message msg):
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發送消息最後的回調)
- mCallback接口執行handleMessage方法(這個是我們在構造Handler時候添加和Callback情況下的回調)。
- handleMessage(這個是我們重寫和Handler.handleMessage情況下的回調)。
講到這裏,相信大家對我們Handler的運行機制,心裏已經透徹了一些,但是還沒完,我們要知道我們在主線程中(MainActivity中的實例變量爲例),他就不用調用Looper.prepare和Looper.loop方法,我們看一下ActivityThread的main方法就一目瞭然了:
public static void main(String[] args) {
......
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我只留下了這兩個方法,和最後的異常:我們在運行之初,就已經給主線程進行了prepare(prepareMainLooper本質還是調用prepare,大家下去自己去看)和loop方法,所以說一旦loop方法調用之後,不會終止,這是一個真的無效輪詢過程。否則就會拋出如上異常。
大家還記得我之前說的嗎:相比looper,真正輪詢的工作另有其人。可能現在這麼說已經不太對了。就目前來看,我們在運行時有兩個無線輪詢:loop方法中和next方法中。如果說他們都是輪詢的工作的話,那麼looper是進行着不同線程之間的輪詢(通過獲取當前線程來獲取當前輪詢器,調用對應的消息隊列進行消息輪詢),而MessageQueue的輪詢,是當前線程的消息輪詢(如上所述-。=)。
Handler機制總結:
非主線程情況,需要調用Looper.prepaore和Looper.loop方法:Looper.prepare方法給當前線程綁定一個Looper對象(本質是Thread中有ThreadLocalMap引用,而Looper中也有Thread引用,從而實現了雙向關聯)。Looper.loop方法進入無限輪詢,不斷地調用當前線程的Looper對象的mQueue對象的next方法(有點拗口)。當獲取到消息時,就分發給對應的handler去執行(通過三種回調,具體看上面)
幾張很有用的圖-。+:
這麼多文字,相信大家已經看得想吐了,最後給大家帶來幾張圖吧。這張圖對Handler的輪詢機制是很形象,但是希望大家不要吐槽我的畫圖能力-。+:
傳送帶,電池。。。。都是大家能看懂的東西,領會精神哈!
這個是剛纔我們Looper相關類的類圖。
最後一張送上涵蓋我們上述的主要的類的一張類圖。(剛學UML,畫的可能有點迷,見諒!!)。
喜歡的朋友希望關注一下,如果有不同的觀點或者指出錯誤歡迎下方留言。