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時,構建消息循環隊列,并獲取當前線程
- 但該函數是私有的,外界不能直接構造一個Looper,而是通過Looper.prepare來構造的:
-
這里創建Looper,并把Looper對象保存在sThreadLocal中,那sThreadLocal是什么呢?
static final ThreadLocal sThreadLocal = new ThreadLocal();
它是一個保存Looper的TheadLocal實例,而ThreadLocal是線程私有的數據存儲類,可以來保存線程的Looper對象,這樣Handler就可以通過ThreadLocal來保存于獲取Looper對象了。 - TheadLocal 如何保存與獲取Looper?
接下來看Looper在loop中如何處理消息
在loop中,一個循環,通過next取出MessageQueue中的消息
-
若取出的消息為null,則結束循環,返回。
- 設置消息為空,可以通過MessageQueue的quit和quitSafely方法通知消息隊列退出。
- 若取出的消息不為空,則通過msg.target.dispatchMessage回調到handler中去。
4、handler處理消息
Looper把消息回調到handler的dispatchMessage中進行消息處理:
- 若該消息有callback,即通過Post(Runnable)的方式投遞消息,因為在投遞runnable時,把runnable對象賦值給了message的callback。
- 若handler的mCallback不為空,則交由通過callback創建handler方式去處理。
- 否則,由最常見創建handler對象的方式,在重寫handlerMessage中處理。
總結
以一個時序圖來總結handler的消息機制,包含上述如何關聯Looper和MessageQueue的過程。
Handler-Looper-MessageQueue時序圖
總結
以上是生活随笔為你收集整理的Handler 系列二:如何通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux环境安装部署mark
- 下一篇: 基于nginx和uWSGI在Ubuntu