日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

刨根问底——Handler

發布時間:2025/4/16 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 刨根问底——Handler 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
提起Handler,很多人首先想到的就是子線程執行耗時操作,主線程更新UI。那么這種機制內部是怎么實現的呢?為什么我們只需要在UI線程聲明初始化一個Handler,然后在子線程發送一個消息,最后就能根據這條消息來執行后續的操作?其實這種機制不僅僅只是通過Handler來實現的,它還需要Looper、MessageQueue、Message來協同處理。

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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 操女人的逼逼 | 久久逼逼| 污到下面流水的视频 | 日日爽日日操 | 亚洲国产欧美精品 | 自拍色图 | 女女互慰吃奶互揉调教捆绑 | 国产精品色悠悠 | 污污的网站在线免费观看 | 亚洲天堂2014 | 亚洲视频网站在线 | 99久久国 | 麻豆av片| a级片中文字幕 | 亚洲最新偷拍 | 毛片在线免费观看网站 | 欧美性受xxxx黑人xyx | 国产视频一二区 | 成人精品一区二区三区电影黑人 | 毛片com| 91视频影院 | 懂色一区二区 | 国产做受高潮动漫 | 欧美理伦少妇2做爰 | 日本黄色成人 | 开心激情综合 | 成人免费大全 | 日韩久久网 | 成人污视频 | 欧美色图在线播放 | 日本va在线观看 | 国产情侣啪啪 | 拍摄av现场失控高潮数次 | av免费观看网站 | 国产精品一区二区三区四区 | 天天亚洲| 婷婷成人综合网 | 人人妻人人澡人人爽人人精品 | 精品人妻一区二区三区潮喷在线 | 日韩欧美高清视频 | 99热这里只有精品4 精品国产黄色 | 欧美多p| 国精产品一区一区三区有限公司杨 | 国产精品久久久爽爽爽麻豆色哟哟 | 久久成人毛片 | 女人叫床很黄很污句子 | 波多野结衣在线一区二区 | 性一交一乱一乱一视频 | 日韩爱爱网站 | 就去吻综合 | 91小视频在线 | 精品不卡一区 | 精东传媒在线观看 | 一本色道久久亚洲综合精品蜜桃 | 在线播放的av | 久久亚洲综合 | 久久激情网| 国产91精品看黄网站在线观看 | 欧美一级片黄色 | 97成人精品视频在线观看 | 日本精品视频一区二区 | 三级色网| 亚洲天堂777 | 四虎网站在线 | 欧美精品成人一区二区在线观看 | 国产日韩欧美中文字幕 | 欧美精品亚洲 | www.成人网.com | 色综合久久久久综合体桃花网 | 色欲狠狠躁天天躁无码中文字幕 | 国产精品福利网站 | 日韩少妇诱惑 | 亚洲av无码精品色午夜 | 亚洲色视频 | 麻豆精品影院 | 依人综合网 | 一区二区三区播放 | 人妻在线日韩免费视频 | 亚洲伊人天堂 | 三级视频网 | 午夜激情欧美 | 欧美一区二区三区公司 | 欧美精品国产精品 | 国产一区二区三区影院 | 成人免费毛片日本片视频 | 九九久久99 | 国家队动漫免费观看在线观看晨光 | 国产精品亚洲欧美 | 久久午夜精品视频 | 欧美日韩亚洲在线 | 欧美精品在线第一页 | 丝袜人妻一区二区 | 羞羞涩涩视频 | 欧美成人女星 | 玖玖综合网 | 亚洲欧美精品久久 | 色中文字幕 | 欧洲中文字幕日韩精品成人 | 国产特黄大片aaaa毛片 |