国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Handler 系列二:通信

2019-11-06 09:38:17
字體:
來源:轉載
供稿:網友

承接上一篇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.next            prev.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<Looper> sThreadLocal = new ThreadLocal<Looper>();

它是一個保存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)獲取,其中putgetAfterMiss都有keyvalue,都是由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 block        if (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 logger        Printer 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時序圖


上一篇:Json解析


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 禹州市| 二手房| 论坛| 云阳县| 靖远县| 丘北县| 法库县| 美姑县| 布尔津县| 噶尔县| 旬邑县| 化德县| 云阳县| 阳山县| 扶绥县| 黑河市| 麻阳| 晋宁县| 铜陵市| 柳江县| 上高县| 瑞丽市| 五华县| 洱源县| 崇礼县| 漳平市| 英德市| 双牌县| 沙田区| 湖州市| 广昌县| 阿荣旗| 武鸣县| 鄂伦春自治旗| 额尔古纳市| 滁州市| 冕宁县| 东乡| 泰顺县| 北流市| 上思县|