Handler原理分析
Handler的原理分析這個(gè)標(biāo)題,很多文章都寫過,最近認(rèn)真將源碼逐行一字一句研究,特此也簡單總結(jié)一遍。
首先是Handler整個(gè)Android消息機(jī)制的簡單概括:
分三部分對(duì)消息機(jī)制的整個(gè)流程進(jìn)行闡述:
- Handler的創(chuàng)建,包括Looper、MessageQueue的創(chuàng)建;
- Handler發(fā)送消息,Message是如何進(jìn)入消息隊(duì)列MessageQueue的(入列);
- Looper輪詢消息,Message出列,Handler處理消息。
一、Handler創(chuàng)建流程分析
1.Handler如何被創(chuàng)建的
// 最簡單的創(chuàng)建方式 public Handler() {this(null, false); }// ....還有很多種方式,但這些方式最終都執(zhí)行這個(gè)構(gòu)造方法 public Handler(Callback callback, boolean async) {// 1.檢查內(nèi)存泄漏if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}// 2.通過Looper.myLooper()獲取當(dāng)前線程的Looper對(duì)象mLooper = Looper.myLooper();// 3.如果Looper為空,拋出異常if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async; } 復(fù)制代碼首先,如何避免Handler的內(nèi)存泄漏是一個(gè)非常常見的面試題,其實(shí)Handler的源碼中已經(jīng)將答案非常清晰告知給了開發(fā)者,即讓Handler的導(dǎo)出類保證為static的,如果需要,將Context作為弱引用的依賴注入進(jìn)來。
同時(shí),在Handler創(chuàng)建的同時(shí),會(huì)嘗試獲取當(dāng)前線程唯一的Looper對(duì)象:
public final class Looper {static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();public static Looper myLooper() {return sThreadLocal.get();} } 復(fù)制代碼關(guān)于ThreadLocal,我在上一篇文章中已經(jīng)進(jìn)行了分析,現(xiàn)在我們知道了ThreadLocal保證了當(dāng)前線程內(nèi)有且僅有唯一的一個(gè)Looper。
2.Looper是如何保證線程單例的
那就是需要調(diào)用Looper.prepare()方法:
public final class Looper {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));}private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}} 復(fù)制代碼這也就說明了,為什么當(dāng)前線程沒有Looper的實(shí)例時(shí),會(huì)拋出一個(gè)異常并提示開發(fā)者需要調(diào)用Looper.prepare()方法了。
也正如上述代碼片段所描述的,如果當(dāng)前線程已經(jīng)有了Looper的實(shí)例,也會(huì)拋出一個(gè)異常,提示用戶每個(gè)線程只能有一個(gè)Looper(throw new RuntimeException("Only one Looper may be created per thread");)。
此外,在Looper實(shí)例化的同時(shí),也創(chuàng)建了對(duì)應(yīng)的MessageQueue,這也就說明,一個(gè)線程有且僅有一個(gè)Looper,也僅有一個(gè)MessageQueue。
二、發(fā)送消息流程分析
1.sendMessage()分析
sendMessage()流程如下:
// 1.發(fā)送即時(shí)消息 public final boolean sendMessage(Message msg) {return sendMessageDelayed(msg, 0); }// 2.實(shí)際上是發(fā)射一個(gè)延時(shí)為0的Message public final boolean sendMessageDelayed(Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }// 3.將消息和延時(shí)的時(shí)間進(jìn)行入列(消息隊(duì)列) 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); }// 4.內(nèi)部實(shí)際上還是執(zhí)行了MessageQueue的enqueueMessage()方法 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis); } 復(fù)制代碼注意第四步實(shí)際上將Handler對(duì)象最為target,附著在了Message之上;接下來看MessageQueue類內(nèi)部是如何對(duì)Message進(jìn)行入列的。
2.MessageQueue消息入列
boolean enqueueMessage(Message msg, long when) {//... 省略部分代碼synchronized (this) {msg.markInUse();msg.when = when;// 獲得鏈表頭的MessageMessage p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// 若有以下情景之一,將Message置于鏈表頭// 1.頭部Message為空,鏈表為空// 2.消息為即時(shí)Message// 3.頭部Message的時(shí)間戳大于最新Message的時(shí)間戳msg.next = p;mMessages = msg;needWake = mBlocked;} else {// 反之,將Message插入到鏈表對(duì)應(yīng)的位置Message prev;// for循環(huán)就是找到合適的位置,并將最新的Message插入鏈表for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}if (needWake) {nativeWake(mPtr);}}return true; } 復(fù)制代碼MessageQueue的數(shù)據(jù)結(jié)構(gòu)本身是一個(gè)單向鏈表。
三、接收消息分析
當(dāng)Handler創(chuàng)建好后,若在此之前調(diào)用了Looper.prepare()初始化Looper,還需要調(diào)用Looper.loop()開始該線程內(nèi)的消息輪詢。
1.Looper.loop()
public static void loop() {// ...省略部分代碼// 1. 獲取Looper對(duì)象final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}// 2.獲取messageQueuefinal MessageQueue queue = me.mQueue;// 3. 輪詢消息,這里是一個(gè)死循環(huán)for (;;) {// 4.從消息隊(duì)列中取出消息,若消息隊(duì)列為空,則阻塞線程Message msg = queue.next();if (msg == null) {return;}// 5.派發(fā)消息到對(duì)應(yīng)的Handlermsg.target.dispatchMessage(msg);// ...} } 復(fù)制代碼比較簡單,需要注意的一點(diǎn)是MessageQueue.next()是一個(gè)可能會(huì)阻塞線程的方法,當(dāng)有消息時(shí)會(huì)輪詢處理消息,但如果消息隊(duì)列中沒有消息,則會(huì)阻塞線程。
2.MessageQueue.next()
private native void nativePollOnce(long ptr, int timeoutMillis);Message next() {// ...省略部分代碼int nextPollTimeoutMillis = 0;for (;;) {// ...// native方法nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;// 從消息隊(duì)列中取出消息if (msg != null) {// 當(dāng)時(shí)間小于message的時(shí)間戳?xí)r,獲取時(shí)間差if (now < msg.when) {// 該值將會(huì)導(dǎo)致在下次循環(huán)中阻塞對(duì)應(yīng)時(shí)間nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// 取出消息并返回mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;msg.markInUse();return msg;}}// ...} } 復(fù)制代碼注意代碼片段最上方的native方法——循環(huán)體內(nèi)首先調(diào)用nativePollOnce(ptr, nextPollTimeoutMillis),這是一個(gè)native方法,實(shí)際作用就是通過Native層的MessageQueue阻塞nextPollTimeoutMillis毫秒的時(shí)間:
- 1.如果nextPollTimeoutMillis=-1,一直阻塞不會(huì)超時(shí)。
- 2.如果nextPollTimeoutMillis=0,不會(huì)阻塞,立即返回。
- 3.如果nextPollTimeoutMillis>0,最長阻塞nextPollTimeoutMillis毫秒(超時(shí)),如果期間有程序喚醒會(huì)立即返回。
搞清楚這一點(diǎn),其它就都好理解了。
3.最終將消息發(fā)送給Handler
正如上文所說的,msg.target.dispatchMessage(msg)實(shí)際上就是調(diào)用Handler.dispatchMessage(msg),內(nèi)部最終也是執(zhí)行了Handler.handleMessage()回調(diào):
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}// 如果消息沒有定義callBack,或者不是通過// Handler(Callback)的方式實(shí)例化Handler,// 最終會(huì)走到這里handleMessage(msg);} } 復(fù)制代碼參考&感謝
- 《Android開發(fā)藝術(shù)探索》
- 深入理解MessageQueue
- Android Handler:手把手帶你深入分析 Handler機(jī)制源碼
關(guān)于我
Hello,我是卻把清梅嗅,如果您覺得文章對(duì)您有價(jià)值,歡迎 ??,也歡迎關(guān)注我的博客或者Github。
如果您覺得文章還差了那么點(diǎn)東西,也請(qǐng)通過關(guān)注督促我寫出更好的文章——萬一哪天我進(jìn)步了呢?
- 我的Android學(xué)習(xí)體系
- 關(guān)于文章糾錯(cuò)
- 關(guān)于知識(shí)付費(fèi)
轉(zhuǎn)載于:https://juejin.im/post/5ca38fbdf265da30b77379c1
總結(jié)
以上是生活随笔為你收集整理的Handler原理分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2014读书计划
- 下一篇: ios runloop学习