日韩性视频-久久久蜜桃-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的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 久久丝袜视频 | 九九热久久免费视频 | 精品丰满少妇一区二区三区 | 一二三区精品视频 | 99热这里只有精品66 | 亚洲精选中文字幕 | 久久婷婷伊人 | 黄色伊人网 | 66精品 | 深爱开心激情 | 黄色免费一级视频 | 欧美乱欲视频 | 久热伊人 | 成人小视频在线免费观看 | 日本精品区 | 色一区二区三区四区 | 欧美成人中文字幕 | 亚洲欧美一区二区三区四区 | 午夜精品欧美 | 久久精品国产av一区二区三区 | 狠狠干av| 99自拍视频 | 深夜福利视频在线 | 亚洲欧洲综合av | 色噜噜在线播放 | 亚洲精品久久久久久久久久久 | 亚洲激情在线视频 | 三级免费网址 | 一级国产视频 | 国产永久免费 | 爱福利视频广场 | 影音先锋人妻啪啪av资源网站 | 亚洲第一黄色片 | 日本a级片网站 | 久久国产一区二区三区 | 色先锋在线| 欧美一级做a爰片免费视频 成人激情在线观看 | 人人草人人搞 | 国产91亚洲精品 | 日韩不卡在线观看 | 亚洲精品乱码久久久久久久久久久久 | 午夜视频网站 | 日产国产亚洲精品系列 | 一区二区三区激情视频 | 色婷婷狠 | 亚洲欧美日韩国产一区 | 激情视频在线播放 | 欧美成人精品激情在线视频 | 一本色道综合久久欧美日韩精品 | 中国一极毛片 | av丁香| 国产综合在线观看 | 荷兰女人裸体性做爰 | 特黄网站 | 久久激情综合 | 高柳家在线观看 | 亚洲一二三不卡 | 亚洲天堂爱爱 | 欧美精品电影一区二区 | 国产日韩视频一区 | 熟女高潮一区二区三区视频 | 亚洲三级理论 | 欧美野外猛男的大粗鳮 | 夜夜夜操 | 亚洲 小说 欧美 激情 另类 | 夜夜爱av| 成人精品福利 | 动漫美女视频 | 久久久免费观看视频 | 五月婷婷综合在线观看 | 深夜国产视频 | 悠悠av | 免费在线观看黄视频 | 伊人影院亚洲 | 日本美女三级 | a视频在线观看 | 超碰精品在线观看 | 日日撸视频 | 国产一区二区三区免费观看视频 | 成人免费视频国产免费麻豆 | 99riav国产在线观看 | 少妇做爰免费视频播放 | 中文字幕一区二区三区手机版 | 中国毛片网站 | 欧美综合视频在线观看 | 麻豆日产六区 | 日韩欧美一二三 | 国产精品网站免费 | 日韩欧美一区二区在线 | 久久精品99国产国产精 | 久久久久久久久久久久久久免费看 | 五月婷婷一区 | 欧美日韩一级二级三级 | 国产中文字幕一区二区 | 国产免费看 | 欧美大色一区 | 亚洲第一免费视频 | 国内一区二区视频 | 日韩精品久久久久久久电影99爱 |