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