-
Handler:Android SDK 提供給開(kāi)發(fā)者方便進(jìn)行異步消息處理的類(面試都會(huì)問(wèn)原理)
image.png - messageQueue 消息隊(duì)列: 先進(jìn)先出。管理Message對(duì)象,需要注意的是 messageQueue消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)并不是隊(duì)列,它是由單鏈表來(lái)構(gòu)成的
整個(gè)流程: 首先為主線程創(chuàng)建一個(gè)looper對(duì)象,每一個(gè)線程只能有一個(gè)looper。而在創(chuàng)建looper對(duì)象的時(shí)候它內(nèi)部構(gòu)造方法中又會(huì)創(chuàng)建一個(gè)messageQueue對(duì)象。而創(chuàng)建handler的時(shí)候我們會(huì)取出當(dāng)前線程的looper,然后通過(guò)looper去輪詢messageQueue中的message。handler每發(fā)送一條消息,相當(dāng)于在messageQueue中添加一條消息。最后通過(guò)looper當(dāng)中的消息循環(huán)取得消息隊(duì)列中的message交給handler去進(jìn)行處理。

通過(guò)源碼可以發(fā)現(xiàn),創(chuàng)建handler的時(shí)候。會(huì)調(diào)用一個(gè)Looper.myLooper()方法,如果looper沒(méi)走looper.prepare,則會(huì)報(bào)異常。


我們可以看到 looper通過(guò)sThreadLocal這個(gè)容器來(lái)存放。如果創(chuàng)建了就會(huì)提示一個(gè)looper只能創(chuàng)建一次。否則則將這個(gè)looper對(duì)象放到sThreadLoacl中,sThreadLoacl我們也叫作線程本地變量。它能為每個(gè)變量在每個(gè)線程都創(chuàng)建一個(gè)副本,這樣就能保證每個(gè)線程都能訪問(wèn)自己的變量。所以說(shuō)handler必須得有一個(gè)指定的looper對(duì)象。而創(chuàng)建完looper之后,looper會(huì)創(chuàng)建消息隊(duì)列,之后賦值給handler的mQueue。三者做一個(gè)捆綁。而在looper構(gòu)造方法中,我們也看到了他new 了一個(gè) messageQueue對(duì)象。
現(xiàn)在我們來(lái)看看Looper.myLooper()方法
image.png
通過(guò)上面所說(shuō)的,sThreadLoacl是線程所持有的容器。所以通過(guò)他的get方法就能獲得looper對(duì)象
再看sendEmptyMessage還是sendEmptyMessageDelayed 最終都會(huì)走到一個(gè)enqueueMessage方法。這個(gè)方法的就是將我們的message添加到消息隊(duì)列當(dāng)中。
image.png
接下來(lái)我們?cè)倏?em>looper這個(gè)類
/**
* 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();
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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
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);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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();
}
}
for (;;)是個(gè)死循環(huán),意思是looper輪詢器會(huì)不斷的去輪詢消息。如果消息不為空,他就會(huì)執(zhí)行 msg.target.dispatchMessage(msg);來(lái)處理消息 其中msg.target可以看上面一張圖 enqueueMessage的方法。我們可以知道,這個(gè)就是我們的handler對(duì)象
我們回到handler類看這個(gè)方法:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
他會(huì)先判斷msg的callback是不是為空,如果不為空他就會(huì)執(zhí)行 handleCallback(msg);那么這個(gè) callback 是什么呢?其實(shí)他就是我們的runnable。所以說(shuō)不管handler最終post了什么。最終他傳遞的都是一個(gè)runnable參數(shù),我們可以點(diǎn)進(jìn)去看下他的方法如下:
private static void handleCallback(Message message) {
message.callback.run();
}
就是調(diào)用了runnable的run方法
接著我們繼續(xù)看如果msg的callback為空的情況下,他會(huì)用mCallback.handleMessage(msg)來(lái)處理消息。那么這個(gè)mCallback又是什么呢?
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
我們?cè)谠创a中發(fā)現(xiàn)他是一個(gè)接口,而他的接口中呢只有一個(gè)我們特別熟悉的方法handleMessage。用來(lái)處理消息。
主線程的死循環(huán)一直運(yùn)行是不是特別消耗CPU資源呢? 其實(shí)不然,這里就涉及到Linux pipe/e****poll機(jī)制,簡(jiǎn)單說(shuō)就是在主線程的MessageQueue沒(méi)有消息時(shí),便阻塞在loop的queue.next()中的nativePollOnce()方法里,詳情見(jiàn)Android消息機(jī)制1-Handler(Java層),此時(shí)主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài),直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生,通過(guò)往pipe管道寫(xiě)端寫(xiě)入數(shù)據(jù)來(lái)喚醒主線程工作。這里采用的epoll機(jī)制,是一種IO多路復(fù)用機(jī)制,可以同時(shí)監(jiān)控多個(gè)描述符,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮?,本質(zhì)同步I/O,即讀寫(xiě)是阻塞的。 所以說(shuō),主線程大多數(shù)時(shí)候都是處于休眠狀態(tài),并不會(huì)消耗大量CPU資源。
下面總結(jié)幾點(diǎn)
- 1.Lopper 類主要是為每個(gè)線程開(kāi)啟的單獨(dú)的消息循環(huán)。默認(rèn)情況下Android新誕生的線程是沒(méi)有開(kāi)啟消息循環(huán)的,所以說(shuō)要在線程中使用Handler,你必須先調(diào)用Looper.prepare()方法。但是主線程除外,因?yàn)橹骶€程中系統(tǒng)已為我們創(chuàng)建了Looper對(duì)象**
- 2.handler 我們可以看作是Looper的一個(gè)接口,用來(lái)像Looper指定的messageQueue 發(fā)送消息
- 3.在非主線程中直接new Handler()是不可以的。原理:Handler他需要發(fā)送消息到MessageQueue,而你所在的線程中沒(méi)有MessageQueue,而MessageQueue由Looper管理,你想創(chuàng)建Handler,你必須先創(chuàng)建好Looper。


