Handler 在開發(fā)中使用的非常平凡,它基于一種隊(duì)列消息機(jī)制,循環(huán)處理消息或者開發(fā)者的代碼任務(wù),實(shí)現(xiàn)不同線程之間的通信,下面結(jié)合源碼學(xué)習(xí)其如何實(shí)現(xiàn)。
-
主線程如何建立Handler消息機(jī)制
當(dāng)我們點(diǎn)擊launch的app圖標(biāo)時(shí),系統(tǒng)會做什么,系統(tǒng)肯定會首先創(chuàng)建一個(gè)進(jìn)程,為該app獨(dú)有,接著,肯定是主線程了,也就是平常經(jīng)常說的UI線程,不能執(zhí)行耗時(shí)操作...,根據(jù)java的特性,肯定會執(zhí)行一個(gè)類的main方法,這里,系統(tǒng)執(zhí)行了ActivityThread的main方法。
點(diǎn)開ActivityThread類,main如下(代碼太長,省略部分):
ActivityThread:
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");
}
首先調(diào)用了Looper的prepareMainLooper(),所以開發(fā)者就不需要在主線程調(diào)用Looper.prepare()直接使用handle,該靜態(tài)方法內(nèi)容如下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//對開發(fā)者提供的prepare(),默認(rèn)傳true,下面會分析
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));
}
和咱調(diào)用Looper一樣,主線程的Looper也是得先prepare,但傳入了false值,深入prepare()源碼,可以發(fā)現(xiàn),該false值最終傳入了Looper對象所維護(hù)的MessageQueue中的mQuitAllowed變量中,而在MessageQueue中作用是什么呢,ctrl+f,查找mQuitAllowed使用過的地方...發(fā)現(xiàn)僅在下面這些地方出現(xiàn)過:
MessageQueue:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
噢了,用來防止開發(fā)者終止主線程的消息隊(duì)列的,停止Looper循環(huán)的。繼續(xù)Looper的prepareMainLooper(),在Looper prepare完后,就是創(chuàng)建Looper對象了,并且存在了Looper靜態(tài)變量中。這樣主線程的Looper就算準(zhǔn)備完畢,Looper的消息隊(duì)列MessageQueue也創(chuàng)建處理,作為傳送帶的角色隨時(shí)待命開機(jī)傳送...
再回到ActivityThread的main方法,在 Looper.prepareMainLooper()執(zhí)行完畢后,就輪到Handler處理者角色創(chuàng)建啦,作為一套成型的消息處理機(jī)制,Handler作為消息的消費(fèi)者,地位肯定不可或缺。
再分析下main方法,在執(zhí)行 sMainThreadHandler = thread.getHandler();之前,主線程創(chuàng)建了ActivityThread對象并調(diào)用了attach函數(shù),這里干嘛吶,這里就涉及到應(yīng)用的初始化啦,比如Application創(chuàng)建,Activity創(chuàng)建,執(zhí)行應(yīng)用的生命周期啊等等,挺復(fù)雜,還沒轉(zhuǎn)過彎...反正里面的操作也是向主線程消息隊(duì)列發(fā)送消息的。
但是這里可以想下,既然這里send了Message,但Looper還沒有開始啟動MessageQueue循環(huán)處理啊,有用嗎?這里再一步步深入Handler源碼的sendMessage方法,關(guān)鍵代碼如下:
Handle:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue:
boolean enqueueMessage(Message msg, long when) {
...
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;
}
...
return true;
}
可以看出,Handle的sendMessage是將Message send進(jìn)了MessageQueue,enqueueMessage()方法中就是執(zhí)行隊(duì)列的操作,將Message加入一個(gè)隊(duì)列中了,就相當(dāng)于一個(gè)個(gè)的放上傳送帶了并且通過next引用互相聯(lián)系著,通過for(;;)循環(huán),找到隊(duì)列的最后個(gè)Message,然后將最后一個(gè)的Message的next指針指向新來的msg,鏈表結(jié)構(gòu)哈。
這樣就清楚上面的疑惑了,傳送帶還沒開動,但先把物品給放上去排好,待會跑起來了,再一個(gè)個(gè)給處理,一點(diǎn)不耽誤。
啥時(shí)候跑起來呢,繼續(xù)擼咱的main方法,好了,說曹操曹操就到,接下來就是 Looper.loop();傳送帶開關(guān)開了,老司機(jī)開車了,Looper.loop()源碼:
Looper:
/**
* 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();
// 開始在當(dāng)前線程中循環(huán)...
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
為什么要循環(huán)起來呢,因?yàn)榫€程的生命周期限制于它的有效代碼行數(shù),循環(huán)起來,有效代碼相對增多了,生命周期就延長了,這也保證了app能一直運(yùn)行了,這也是主線程的Looper調(diào)用quit()會拋異常的原因,主線程quit了app還怎么玩...
開始 loop之前,要獲取當(dāng)前線程持有的Looper對象,有嗎,沒有就果斷拋異常,有?拿出Looper的MessageQueue傳送帶來,接下來for循環(huán)開啟傳送帶開關(guān),運(yùn)轉(zhuǎn)起來了。
這里為啥用for(;;)而不是while呢,有說習(xí)慣問題,有說防止被修改字節(jié)碼,也有人說比while少個(gè)判斷,具體也是不清楚,還請高人指點(diǎn)...
接下來循環(huán)體就直截了當(dāng)了,MessageQueue的next是阻塞方法,在這里等待Handle send的Message,來一個(gè)message,就放行一次,然后從message中獲取Handler引用,讓handle dispatchMessage,dispatchMessage也不復(fù)雜:
Handle:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
先判斷有沒有Runnable類型消息,有就執(zhí)行run回調(diào)唄,沒有就走實(shí)例化Handler時(shí)重寫的handleMessage,里面就根據(jù)Message類型及參數(shù)來執(zhí)行不同邏輯。
到這里就建立好了主線程的消息處理機(jī)制,應(yīng)用層開發(fā)者直接通過實(shí)例化Handle sendMessage或者postRunnable就可以向主線程發(fā)送消息了。
-
主線程不能有多余5秒的耗時(shí)操作
都知道主線程中不能有超過5秒的無響應(yīng)操作,否者就ANR(Application Not Responding)了,結(jié)合上面loop()源碼的分析,既然主線程一直阻塞在等待send的message,那么,計(jì)時(shí)系統(tǒng)應(yīng)該在msg.target.dispatchMessage(msg); 處理message方法的前后加的,在執(zhí)行前,開始計(jì)時(shí),當(dāng)達(dá)到5秒時(shí),跳出ANR異常,所有,初步猜想可能和Trace這個(gè)類有關(guān),還請指點(diǎn)!
-
子線程創(chuàng)建Handler消息機(jī)制
子線程創(chuàng)建Handler消息機(jī)制,步驟和主線程的一樣,先Looper.prepare(),來為當(dāng)前線程綁定新的Looper對象,同時(shí),Looper對象會實(shí)例化持有的MessageQueue,然后就可以通過實(shí)例化Handler發(fā)送Message了,但這時(shí)發(fā)了是處理不了的,Looper需要通過loop循環(huán)當(dāng)前的MessageQueue,一個(gè)個(gè)取Message。
所有,子線程有上面操作需要注意啦,loop()之后,子線程生命周期就延長了,除非調(diào)用Looper.quit(),就結(jié)束循環(huán)了。
-
為何子線程中直接使用Toast報(bào)錯(cuò)
翻看Toast源碼,可以看到Toast底層也是用了Handler機(jī)制發(fā)送消息,這就不難理解了,Handler消息機(jī)制需要依附于當(dāng)前線程的Looper對象來運(yùn)作,子線程沒有自己的Looper,Handler也就使用報(bào)錯(cuò),Toast也就不能用啦。
具體參看:http://blog.csdn.net/saroll57/article/details/23276275
-
為何UI操作要在主線程中
UI操作不是線程安全的,比如View的invalidate()就是非線程安全的,View的各種操作最終都會導(dǎo)致invalidate()函數(shù)執(zhí)行,多個(gè)線程執(zhí)行同一UI操作就會導(dǎo)致同時(shí)有多次刷新,就會出現(xiàn)異常情況。
子線程操作UI,可以通過獲取主線程handler的引用發(fā)送message來實(shí)現(xiàn),或者通過:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)