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