日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Handler原理分析

發(fā)布時間:2025/3/17 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Handler原理分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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 @Nullable 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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。