Handler源碼解讀


你們面試的時候是否常常被問到有沒有看過handler源碼,若是回答看過了接下來就各類深刻提問,直到問得你雲裏霧裏面試官纔開心。今天做者帶領你們一塊兒深刻地過一遍handler源碼,將面試經常使用問題各個擊破,今後手捧offer,和麪試失敗say no!


看源碼首先固然是從使用處看起,咱們首先來看一下平時項目中handler的使用,咱們首先定義了一個handler對象,而且寫好了接收到消息之後須要執行的操做,而後在子線程中發送一個消息。面試


問題一  handler發送的消息是怎麼到達handlerMessage回調的呢?
數據結構


首先咱們就從源頭開始看起,handler.senMessage()方法一路點下去,發現最終調用的是MessageQueue類的enqueueMessage方法,也就是說最終是把message添加到了消息隊列MessageQueue中,以下圖所示app


OK,消息放進去了,可是咱們發現這裏面並無調用handler的handleMessage方法,因此咱們換個角度想,放進去了總要取出來吧,否則放進去就沒有意義了。因而咱們就在MessageQueue中尋找返回值爲Message的方法,果不其然找到了一個next方法,在該方法中返回了剛剛保存好的message,以下圖函數


那麼問題又來了,到底是誰調用了這個next方法把消息取出來了呢?解決這個問題就要反過來考慮,須要調用next方法就須要實例化MessageQueue這個類,否則無法調用,MessageQueue是在Looper中初始化的,因此接下來咱們來看看Looper類的源碼。在Looper源碼中是在構造函數中初始化MessageQueue的oop

接下來咱們再搜一下調用next方法的地方
spa



咱們發如今Looper的loop方法中有一個無限循環,其中每次循環都調用MessageQueue的next方法,若是找到了就會執行msg.target.dispatchMessage(msg),那麼msg.target是什麼東西呢?咱們接下來就點開Message類,查看一下target屬性,以下線程

哇塞,原來是Handler對象,這裏其實就是調用的handler的handlerMessage方法,如今邏輯就清晰了。Handler首先將咱們發送的message保存到MessageQueue,而後Looper獲取到message之後調用Handler的handlerMessage方法將message返回。3d


問題二 Handler是如何實現跨線程通訊的?指針

縱覽全局,咱們發現,其實message至關於一個物品,MessageQueue至關於一個倉庫,被handler放到MesageQueue,而後被Looper取出來還給handler,對於物品來講是不會挑選主人的,類的屬性也同樣,不管哪一個線程均可以操做修改。因此message在子線程被放進去之後在主線程被取出來就很正常了。
cdn



問題三 Looper是在哪裏被調用的?

咱們平時使用Handler通常是子線程和主線程通訊,而主線程的Looper其實在app初始化的時候就已經初始化而且開始Loop無限循環獲取Message了,具體代碼在ActivityThread的main方法中,其中prepareMainLooper就是初始化Looper,而後還調用了loop方法。以下圖



問題四 如何保證Looper惟一性,一個線程不會出現多個Looper?

咱們都知道,若是同一線程每次都new一個Looper的話就沒辦法保持惟一性。想要知道這個知識點,必須看一下Looper是怎麼設置和獲取的,咱們點進去初始化的源碼,以下圖

咱們發現,Looper的設置和獲取並非普通的屬性get和set,而是經過ThreadLocal類來操做,因此咱們點進去ThreadLocal看一下,發現裏面Looper的設置和獲取操做都須要傳入線程參數,也就是說以線程爲Key,對應了惟一的Looper,其實Looper惟一也就意味着MessageQueue也是惟一的,由於MessageQueue是在Looper構造方法裏初始化一次的,可是Handler能夠有多個,由於咱們每初始化一個Handler對象,都會被Message記錄,最後調用相應Handler的handleMessage方法,並不會有衝突。其中ThreadLocal代碼以下



問題五 MessageQueue是一個什麼樣的數據結構?

分析這個問題,咱們首先要找到其設置數據的地方,以下圖所示,MessageQueue保存了每條message的值和next指針,也就是說想獲取到下一條消息必需要看上一條消息的next指向,這樣就致使了數據的先進先出,和LinkedList的實現相似,顯然是一個單向鏈表的實現。



問題六 消息是怎麼實現延時的?

1.既然消息是保存在隊列中,那麼剛保存的時候就應該保存好該消息所在的位置纔對,否則在獲取的時候就沒辦法保證延時短的消息先發送了,如問題五中的圖,這個的實現其實就是死循環對每一個message的延時時間進行對比,若是當前消息的延時短則更改指針指向,將當前消息插入到隊列中。

2.若是發送消息的時間沒到,則不交給Looper。以下圖,在Looper獲取消息時會對時間進行判斷,若是時間還沒到則不返回message,即至關於本次循環並無到達時間須要發送的消息



問題七 Handler機制中生產者和消費者模式體如今哪裏?

前面說到handler中把消息放到了messageQueue,而後Looper從messageQueue中獲取消息並使用,這個至關於就是一個生產者和消費者模式,一個負責生產一個負責消費。生產消費者模式中的阻塞在handler中也是體現得淋漓盡致,在獲取消息時,若是消息池中消息數量爲空,那麼此時進入阻塞狀態,以下圖


問題八 爲什麼Looper中死循環不會阻塞UI

首先咱們必須明白的是app自己就是一個死循環,否則豈不是一打開就結束了?其次Looper和更新UI是共存的關係,這裏會有一個底層的睡眠機制,在須要的時候再喚起。

最後:本人小萌新,以前都是看得多寫得少,如今也想把本身的所見所得記錄下來給你們分享分享,如有寫錯或者沒有補充完整的地方歡迎各位大神指教,之後有新的心得體會會及時更新本文,謝謝你們閱讀!

相關文章
相關標籤/搜索