2018-04-12—(重點)源碼角度分析Handler運行原理

今天給大家講一個比較難搞得東西,Handler,其實handler用起來比較方便,流程也不復雜,但是如果我們深入源碼的話,就比較頭大了,今天我們來 一起從源碼角度分析一下handler的工作原理:



一、Handler使用   


首先我們要知道Handler是幹什麼的?

handler主要是用於子線程和主線程之間進行數據交互(通信)。當需要有耗時操作(HTTP請求,數據庫訪問等),我們在子線程完成這些操作,而完成之後通過handler來進行 通信,下面我們來看一下handler的使用:


1.主線程使用handler

在MainActivity中創建一個handler對象。

10608194-6c102c9a3155438b.png
MainActivity新建handler對象

寫一個按鈕

10608194-a905aaa903670789.png
寫一個按鈕,定義點擊事件的方法名字

在上面定義的but方法中,用handler發送一條信息,

10608194-3e8fa65683c0ca28.png
點擊事件方法中發送一個message

然後我們回到handler中,接收這個message

10608194-54b3f27c5c78c16b.png
當有message時候,如果what是0,的執行邏輯

好了,我們來看一下他的效果

10608194-dd45a8aaa8964411.gif
效果

這是在主線程中使用handler,下面我們來看一下在子線程中是怎麼樣使用的:


2.在子線程中使用handler

定義個新的按鈕,

10608194-f0ba1fd0cd4670f3.png
第二個按鈕,

在點擊事件中創建一個新的線程,並且在線程裏面對一個handlerThread初始化。

10608194-7661d5e9d912cc7d.png
第二個按鈕的點擊事件

大家注意到沒,在子線程中,多了兩個方法Looper.prepare()和Looper.loop();兩個方法。這個如果在子線程中使用Handler必須要寫這兩個方法,至於原因我們等一會兒再說。

然後我們分別用handlerThread和handler發送一條消息,

10608194-8b033b3da79e8773.png

爲了看的明顯,我們延時了3秒。

接着我們寫handlerThread中的handleMessage方法。

10608194-e1c62c223d602901.png
handlerThread的handleMessage方法

好了,我們來跑一下,看看效果:

10608194-cd009d111fe2a1a9.gif
效果2

好了, 這是handler的兩種用法,我們已經學會了。




二、從Handler源碼分析Handler工作原理

大家還記得剛纔我們在子線程多加的兩個方法嗎?Looper.prepare()和Looper.loop();這兩個方法是必須要執行的,因爲不是我們主線程不用執行,而是主線程已經自動幫我們執行了這兩個方法了,我們進入main方法看一下:


10608194-d3e813af0438e683.png
main方法
10608194-50c658b52c409821.png
main方法

我們注意到綠色方框框起來的兩個方法正是Looper.prepare()和Looper.loop()。

那麼這兩個方法到底是幹嘛的呢?不要着急,我們首先來看一下Handler的工作流程:


10608194-c6babd5f2c98b154.png
Handler流程圖

這是我們Handler的大概流程圖,我們在線程中用handler通過sendEmptyMessage方法(或者sendMessage方法)發送消息存到MessageQueue中。而我們剛纔的Looper類,是一個輪詢器,它的作用就是一直從MsgQueue中獲取消息,然後將消息發放到handler中,讓他執行邏輯。

我們還是從looper.prepareMainLooper()開始看吧.


10608194-5eb98fab521d8e7c.png
prepareMainLooper方法

我們發現主類中執行的prepareMainLooper方法其實也是執行prepare方法,我們點進prepare方法。


10608194-bb81413428d0a68b.png
prepare方法

他在下面執行了一個ThreadLocal.set(new Looper(...))方法,我們暫時可以理解爲我們在ThreadLocal中添加了一個 當前線程的looper對象。

我們繼續看prepareMainLooper,他之後又執行了一個myLooper方法,我們點進去。

10608194-03c57c8b9024d1fb.png
myLooper方法

這個方法是將我們剛纔存入的當前線程的looper對象取出來。

然後我們來看一下loop方法。


10608194-a7bf4a9566ef4ea1.png
loop方法

我在裏面寫了很清楚的備註,所以當我們執行了loop方法之後,他就會開始無效循環,一直在尋找當前線程的消息隊列中是否有message,

10608194-f636fc9e75fb0b32.png
loop方法

我們接着看,在下面我們輪詢時候消息隊列有msg時候,我們會讓msg的target對象執行dipatchMessage方法,我們看一看target屬性是什麼:


10608194-55d304476899e1b3.png

我們發現他是個Handler類的屬性,其實這個target就是發送msg的那個handler,爲什麼我們下面再說。

我們來接着看一下dispatchMessage方法,

10608194-d53b7999901d61b1.png
dispatchMessage

我們發現原來最後會回調到handleMessage方法中,這個方法我們應該很熟悉了,就是我們剛剛執行操作的方法。至於爲什麼target是我們發送msg的handler呢,我們現在來看一下:

我們從handler.sendMessage方法開始看吧:

10608194-47a45c926a5034ad.png
10608194-1c93ce3f6f3b2b25.png
10608194-9dddcb2e5b67d77c.png
10608194-2a0663b1bbff0559.png

他從sendMessage一直跳轉了這麼幾個方法,在最後的恩queueMessage方法中,我們看到msg.target= this,將當前的handler設爲msg的target,所以target存儲的就是發送msg的handler。

我們來看一下最後跳轉到消息隊列的enqueueMessage方法:


10608194-76e658f86a046cc9.png
MessageQueue的enqueueMessage方法

這個方法其實就是一個鏈表插入,把我們的msg放入到消息隊列中。



所以現在我們知道了,looper的loop方法一旦啓動,就會一直獲取當前線程的looper對象,然後一直輪詢當前對象的消息隊列,當找到消息就發放給msg的handler,讓他執行handleMessage方法,這就是他的工作原理。

下面給大家配上一張邏輯圖和源碼結合的圖:


10608194-99e3d7f29ac368e2.png