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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Handler 系列二:如何通信

發布時間:2025/6/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Handler 系列二:如何通信 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

承接上一篇Handler系列一,上篇主要總結了Handler如何通信,這篇來介紹Handler怎么通信。

  • Handler的通信機制
  • Handler,Looper,MessageQueue如何關聯

Handler 通信機制

  • 創建Handler,并采用當前線程的Looper創建消息循環系統;
  • Handler通過sendMessage(Message)或Post(Runnable)發送消息,調用enqueueMessage把消息插入到消息鏈表中;
  • Looper循環檢測消息隊列中的消息,若有消息則取出該消息,并調用該消息持有的handler的dispatchMessage方法,回調到創建Handler線程中重寫的handleMessage里執行。

Handler如何關聯Looper、MessageQueue

Handler及其關聯的類圖

以上類圖可以快速幫助我們理清Handler與Looper、MessageQueue的關系,以下從源碼的角度慢慢分析:

1、Handler 發送消息

上一段很熟悉的代碼:

Message msg =Message.obtain(); //從全局池中返回一個message實例,避免多次創建message(如new Message)msg.obj = data;msg.what=1; //標志消息的標志handler.sendMessage(msg);

從sendMessageQueue開始追蹤,函數調用關系:sendMessage -> sendMessageDelayed ->sendMessageAtTime,在sendMessageAtTime中,攜帶者傳來的message與Handler的mQueue一起通過enqueueMessage進入隊列了。

對于postRunnable而言,通過post投遞該runnable,調用getPostMessage,通過該runnable構造一個message,再通過 sendMessageDelayed投遞,接下來和sendMessage的流程一樣了。

2、消息入隊列

在enqueueMessage中,通過MessageQueue入隊列,并為該message的target賦值為當前的handler對象,記住msg.target很重要,之后Looper取出該消息時,還需要由msg.target.dispatchMessage回調到該handler中處理消息。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

在MessageQueue中,由Message的消息鏈表進行入隊列

boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue. Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();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.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

3、Looper 處理消息

再說處理消息之前,先看Looper是如何構建與獲取的:
  • 構造Looper時,構建消息循環隊列,并獲取當前線程
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
  • 但該函數是私有的,外界不能直接構造一個Looper,而是通過Looper.prepare來構造的:
public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}
  • 這里創建Looper,并把Looper對象保存在sThreadLocal中,那sThreadLocal是什么呢?

    static final ThreadLocal sThreadLocal = new ThreadLocal();

    它是一個保存Looper的TheadLocal實例,而ThreadLocal是線程私有的數據存儲類,可以來保存線程的Looper對象,這樣Handler就可以通過ThreadLocal來保存于獲取Looper對象了。
  • TheadLocal 如何保存與獲取Looper?
public void set(T value) {Thread currentThread = Thread.currentThread();Values values = values(currentThread);if (values == null) {values = initializeValues(currentThread);}values.put(this, value);}public T get() {// Optimized for the fast path.Thread currentThread = Thread.currentThread();Values values = values(currentThread);if (values != null) {Object[] table = values.table;int index = hash & values.mask;if (this.reference == table[index]) {return (T) table[index + 1];}} else {values = initializeValues(currentThread);}return (T) values.getAfterMiss(this);} 在 set 中都是通過 `values.put` 保存當前線程的 Looper 實例,通過 `values.getAfterMiss(this)`獲取,其中`put`和`getAfterMiss`都有`key`和`value`,都是由Value對象的table數組保存的,那么在table數組里怎么存的呢?table[index] = key.reference;table[index + 1] = value;很顯然在數組中,前一個保存著ThreadLocal對象引用的索引,后一個存儲傳入的Looper實例。
接下來看Looper在loop中如何處理消息

在loop中,一個循環,通過next取出MessageQueue中的消息

  • 若取出的消息為null,則結束循環,返回。

    • 設置消息為空,可以通過MessageQueue的quit和quitSafely方法通知消息隊列退出。
  • 若取出的消息不為空,則通過msg.target.dispatchMessage回調到handler中去。
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 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 loggerPrinter logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}msg.target.dispatchMessage(msg);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();}}

4、handler處理消息

Looper把消息回調到handler的dispatchMessage中進行消息處理:

  • 若該消息有callback,即通過Post(Runnable)的方式投遞消息,因為在投遞runnable時,把runnable對象賦值給了message的callback。
  • 若handler的mCallback不為空,則交由通過callback創建handler方式去處理。
  • 否則,由最常見創建handler對象的方式,在重寫handlerMessage中處理。
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

總結

以一個時序圖來總結handler的消息機制,包含上述如何關聯Looper和MessageQueue的過程。

Handler-Looper-MessageQueue時序圖

總結

以上是生活随笔為你收集整理的Handler 系列二:如何通信的全部內容,希望文章能夠幫你解決所遇到的問題。

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