Handler 系列二:如何通信
承接上一篇Handler系列一,上篇主要總結(jié)了Handler如何通信,這篇來介紹Handler怎么通信。
- Handler的通信機(jī)制
- Handler,Looper,MessageQueue如何關(guān)聯(lián)
Handler 通信機(jī)制
- 創(chuàng)建Handler,并采用當(dāng)前線程的Looper創(chuàng)建消息循環(huán)系統(tǒng);
- Handler通過sendMessage(Message)或Post(Runnable)發(fā)送消息,調(diào)用enqueueMessage把消息插入到消息鏈表中;
- Looper循環(huán)檢測消息隊(duì)列中的消息,若有消息則取出該消息,并調(diào)用該消息持有的handler的dispatchMessage方法,回調(diào)到創(chuàng)建Handler線程中重寫的handleMessage里執(zhí)行。
Handler如何關(guān)聯(lián)Looper、MessageQueue
Handler及其關(guān)聯(lián)的類圖
以上類圖可以快速幫助我們理清Handler與Looper、MessageQueue的關(guān)系,以下從源碼的角度慢慢分析:
1、Handler 發(fā)送消息
上一段很熟悉的代碼:
Message msg =Message.obtain(); //從全局池中返回一個(gè)message實(shí)例,避免多次創(chuàng)建message(如new Message)msg.obj = data;msg.what=1; //標(biāo)志消息的標(biāo)志handler.sendMessage(msg);從sendMessageQueue開始追蹤,函數(shù)調(diào)用關(guān)系:sendMessage -> sendMessageDelayed ->sendMessageAtTime,在sendMessageAtTime中,攜帶者傳來的message與Handler的mQueue一起通過enqueueMessage進(jìn)入隊(duì)列了。
對于postRunnable而言,通過post投遞該runnable,調(diào)用getPostMessage,通過該runnable構(gòu)造一個(gè)message,再通過 sendMessageDelayed投遞,接下來和sendMessage的流程一樣了。
2、消息入隊(duì)列
在enqueueMessage中,通過MessageQueue入隊(duì)列,并為該message的target賦值為當(dāng)前的handler對象,記住msg.target很重要,之后Looper取出該消息時(shí),還需要由msg.target.dispatchMessage回調(diào)到該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的消息鏈表進(jìn)行入隊(duì)列
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是如何構(gòu)建與獲取的:
- 構(gòu)造Looper時(shí),構(gòu)建消息循環(huán)隊(duì)列,并獲取當(dāng)前線程
- 但該函數(shù)是私有的,外界不能直接構(gòu)造一個(gè)Looper,而是通過Looper.prepare來構(gòu)造的:
-
這里創(chuàng)建Looper,并把Looper對象保存在sThreadLocal中,那sThreadLocal是什么呢?
static final ThreadLocal sThreadLocal = new ThreadLocal();
它是一個(gè)保存Looper的TheadLocal實(shí)例,而ThreadLocal是線程私有的數(shù)據(jù)存儲(chǔ)類,可以來保存線程的Looper對象,這樣Handler就可以通過ThreadLocal來保存于獲取Looper對象了。 - TheadLocal 如何保存與獲取Looper?
接下來看Looper在loop中如何處理消息
在loop中,一個(gè)循環(huán),通過next取出MessageQueue中的消息
-
若取出的消息為null,則結(jié)束循環(huán),返回。
- 設(shè)置消息為空,可以通過MessageQueue的quit和quitSafely方法通知消息隊(duì)列退出。
- 若取出的消息不為空,則通過msg.target.dispatchMessage回調(diào)到handler中去。
4、handler處理消息
Looper把消息回調(diào)到handler的dispatchMessage中進(jìn)行消息處理:
- 若該消息有callback,即通過Post(Runnable)的方式投遞消息,因?yàn)樵谕哆frunnable時(shí),把runnable對象賦值給了message的callback。
- 若handler的mCallback不為空,則交由通過callback創(chuàng)建handler方式去處理。
- 否則,由最常見創(chuàng)建handler對象的方式,在重寫handlerMessage中處理。
總結(jié)
以一個(gè)時(shí)序圖來總結(jié)handler的消息機(jī)制,包含上述如何關(guān)聯(lián)Looper和MessageQueue的過程。
Handler-Looper-MessageQueue時(shí)序圖
總結(jié)
以上是生活随笔為你收集整理的Handler 系列二:如何通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux环境安装部署mark
- 下一篇: 基于nginx和uWSGI在Ubuntu