刨根问底——Handler
MessageQueue,顧名思義,就是一個(gè)由Message組成的Queue,它(MessageQueue)的內(nèi)部維護(hù)著一條隊(duì)列,每當(dāng)我們通過Handler發(fā)送一條Message后,這條Message都會(huì)被添加到MessageQueue的隊(duì)列尾部,其實(shí)這條消息隊(duì)列只起到存儲(chǔ)消息的作用,并不具備任何循環(huán)、處理消息的作用。Looper在這個(gè)機(jī)制中扮演者循環(huán)器的作用,它會(huì)不斷的從MessageQueue中取到隊(duì)列頭部的Message,然后交給Handler來處理這條Message。至此,思路已經(jīng)很明顯了,Handler發(fā)送Message至MessageQueue,同時(shí)Looper不斷地嘗試讀取MessageQueue中的Message,發(fā)現(xiàn)Message之后將它取出交由Handler來處理,由于Handler的handleMessage是執(zhí)行在主線程的(假設(shè)我們?cè)谥骶€程初始化的Handler),所以此時(shí)我們可以在該處執(zhí)行一些更新UI的操作。
現(xiàn)在我們來將該機(jī)制分解成三部分:1.Handler發(fā)送Message之后這條Message是怎么進(jìn)入MessageQueue的;2.Looper是如何循環(huán)獲取Message的;3.Message是怎么被Handler處理的。
Handler發(fā)送Message之后Message是怎么進(jìn)入到了MessageQueue中的?說到這個(gè),我們就不得不介紹一下Handler中的sendMessageAtTime這個(gè)方法了,這個(gè)方法我們平時(shí)基本上不會(huì)用到,因?yàn)槲覀冇玫淖疃嗟氖荋andler的sendMessage或者是sendMessageDelayed,又或者是調(diào)用Message的sendToTarget方法(handler.obtainMessage().sendToTarget()),那為什么還要介紹這個(gè)方法呢?因?yàn)槲覀兺ㄟ^Handler和Message的源碼可以看到,無論是調(diào)用上述的哪個(gè)方法,最后都會(huì)走到sendMessageAtTime這個(gè)方法中來,這個(gè)方法中的實(shí)現(xiàn)如下圖
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); }復(fù)制代碼我們可以看到,執(zhí)行到這里的時(shí)候我們已經(jīng)追蹤到了消息進(jìn)入隊(duì)列的入口,我們點(diǎn)進(jìn)enqueueMessage這個(gè)方法中看一下
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis); }復(fù)制代碼我們可以看到,Message是通過這個(gè)方法來將消息放入隊(duì)列的(注意msg.target = this,后面我們會(huì)用到),我們?cè)龠M(jìn)入MessageQueue中來看一下enqueueMessage這個(gè)方法(方法中的一段)
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;復(fù)制代碼在這里,p代表當(dāng)前消息隊(duì)列中的隊(duì)列頭部,prev代表p指向的Message的前一條,我們可以看到,在for循環(huán)中,prev和p一直從隊(duì)列頭部索引到隊(duì)列尾部,當(dāng)跳出for循環(huán)時(shí),p指向的Message為null,說明最后一條消息就是prev這個(gè)變量所指向的Message,當(dāng)最后兩個(gè)表達(dá)式執(zhí)行完畢后,msg被添加到了隊(duì)列的尾部,到這里Message進(jìn)入的MessageQueue中的路線就結(jié)束了。
.Looper是如何循環(huán)獲取Message的前面我們說到,MessageQueue只是Message的容器,它本身不具備選擇Message派發(fā)給相應(yīng)Handler的功能,這時(shí)就需要Looper來發(fā)揮作用了。我們知道Looper被初始化后,都需要調(diào)用loop方法,那么這個(gè)方法是做什么的?我們來看一下
for (;;) {Message msg = queue.next(); // might blockif (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 loggerfinal 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);}}復(fù)制代碼我們來重點(diǎn)關(guān)注一下第2行和倒數(shù)第7行的表達(dá)式,這里面的queue就是MessageQueue了,我們可以看到,Looper在不斷的嘗試從MessageQueue中獲取隊(duì)列頭部的Message,然后會(huì)獲取這條Message的target并且執(zhí)行dispatchMessage()方法,這個(gè)target又是個(gè)啥?進(jìn)入我們剛剛看到過的Handler的enqueueMessage方法中,我們可以看到,在該方法的第1行中就是為Message的target賦值,所以target的值是一個(gè)Handler,也就是我們發(fā)送Message的這個(gè)Handler,咦?好像要到最后一步了,Message已經(jīng)交給Handler了,接下來就是我們最后要說的內(nèi)容了,Handler是怎么處理這條Message的。
Handler是怎么處理這條Message的?現(xiàn)在Message已經(jīng)又交到了Handler的手里,我們來看一下Handler的dispatchMessage方法
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);} }復(fù)制代碼我們可以看到這個(gè)方法的邏輯非常簡(jiǎn)單,如果msg被設(shè)置了回調(diào)方法,就執(zhí)行它的回調(diào)方法,否則就執(zhí)行Handler里面的相關(guān)方法。通常我們并沒有為Message設(shè)置回調(diào)函數(shù),并且都是直接new一個(gè)不帶回調(diào)函數(shù)的Handler,所以我們的方法只剩了最后一條執(zhí)行路線,調(diào)用handleMessage方法,這個(gè)方法看著好眼熟啊!這不就是我們new Handler的時(shí)候?qū)崿F(xiàn)的那個(gè)方法嗎?我們點(diǎn)進(jìn)去看一下
/*** Subclasses must implement this to receive messages.*/ public void handleMessage(Message msg) { }復(fù)制代碼還真是這個(gè)方法。從這里開始就是我們熟悉的操作了,在handleMessage中通過what來匹配,然后來執(zhí)行不同的邏輯,由于我們是在主線程實(shí)現(xiàn)的這個(gè)方法,所以我們可以在這里面更新相關(guān)的UI界面,這樣就實(shí)現(xiàn)了在子線程中執(zhí)行操作,在主線程中更新UI的功能了。
到這里我們Handler的消息派發(fā)機(jī)制大致上已經(jīng)說完了,由于文筆太爛只說了Handler大致的機(jī)制,沒有涉及到的知識(shí)有很多,比如Looper是怎么存儲(chǔ)和獲取的、msg的callback是怎么設(shè)置的等等。我想只要先掌握了機(jī)制的大致流程,以后深入某個(gè)功能的時(shí)候才不會(huì)迷失在海量的代碼中。
寫這些東西只是想對(duì)自己學(xué)過的東西做一下筆記,如果對(duì)您有了一絲絲的幫助,那就再好不過了。多幫我提提建議啊,文中若有不足,敬請(qǐng)諒解!
轉(zhuǎn)載于:https://juejin.im/post/5a4de624f265da43310e3d3e
總結(jié)
以上是生活随笔為你收集整理的刨根问底——Handler的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux的Nginx安装、默认虚拟主机
- 下一篇: 51CTO博客2.0意见反馈处【2018