天天看點

面試:Handler 的工作原理是怎樣的?

面試場景

平時開發用到其他線程嗎?都是如何處理的?

基本都用 RxJava 的線程排程切換,嗯對,就是那個

observeOn

subscribeOn

可以直接處理,比如網絡操作,RxJava 提供了一個叫

io

線程的處理。

在 RxJava 的廣泛使用之前,有使用過其他操作方式嗎?比如 Handler 什麼的?

當然用過呀。

那你講講 Handler 的工作原理吧。

Handler 工作流程基本包括 Handler、Looper、Message、MessageQueue 四個部分。但我們在日常開發中,經常都隻會用到 Handler 和 Message 兩個類。Message 負責消息的搭載,裡面有個

target

用于标記消息,

obj

用于存放内容,Handler 負責消息的分發和處理。

一般在開發中是怎麼使用 Handler 的?

官方不允許在子線程中更新 UI,是以我們經常會把需要更新 UI 的消息直接發給處理器 Handler,通過重寫 Handler 的

handleMessage()

方法進行 UI 的相關操作。

那使用中就沒什麼需要注意的嗎?

有,Handler 如果設定為私有變量的話,Android Studio 會報警告,提示可能會造成記憶體洩漏,這種情況可以通過設定為靜态内部類 + 弱引用,或者在

onDestroy()

方法中調用

Handler.removeCallbacksAndMessages(null)

即可避免;

正文

總的來說這位面試的童鞋答的其實還是沒那麼差,不過細節程度還不夠,是以南塵就來帶大家一起走進 Handler。

Handler 工作流程淺析

異步通信準備 => 消息入隊 => 消息循環 => 消息處理
  1. 異步通信準備

    假定是在主線程建立 Handler,則會直接在主線程中建立處理器對象

    Looper

    、消息隊列對象

    MessageQueue

    和 Handler 對象。需要注意的是,

    Looper

    MessageQueue

    均是屬于其 建立線程 的。

    Looper

    對象的建立一般通過

    Looper.prepareMainLooper()

    Looper.prepare()

    兩個方法,而建立

    Looper

    對象的同時,将會自動建立

    MessageQueue

    ,建立好

    MessageQueue

    後,

    Looper

    将自動進入消息循環。此時,

    Handler

    自動綁定了主線程的

    Looper

    MessageQueue

  2. 消息入隊

    工作線程通過

    Handler

    發送消息

    Message

    到消息隊列

    MessageQueue

    中,消息内容一般是 UI 操作。發送消息一般都是通過

    Handler.sendMessage(Message msg)

    Handler.post(Runnabe r)

    兩個方法來進行的。而入隊一般是通過

    MessageQueue.enqueueeMessage(Message msg,long when)

    來處理。
  3. 消息循環

    主要分為「消息出隊」和「消息分發」兩個步驟,

    Looper

    會通過循環 取出 消息隊列

    MessageQueue

    裡面的消息

    Message

    ,并 分發 到建立該消息的處理者

    Handler

    。如果消息循環過程中,消息隊列

    MessageQueue

    為空隊列的話,則線程阻塞。
  4. 消息處理

    Handler

    接收到

    Looper

    發來的消息,開始進行處理。

對于 Handler ,一些需要注意的地方

  • 1 個線程

    Thread

    隻能綁定 1個循環器

    Looper

    ,但可以有多個處理者

    Handler

  • 1 個循環器

    Looper

    可綁定多個處理者

    Handler

  • 1 個處理者

    Handler

    隻能綁定 1 個 1 個循環器

    Looper

正常情況下,這些相關對象是怎麼建立的?

前面我們說到

Looper

是通過

Looper.prepare()

Looper.prepareMainLooer()

建立的,我們不妨看看源碼裡面到底做了什麼。

我們不得不看看

Looper

的構造方法都做了什麼。

顯而易見,确實在建立了

Looper

對象的時候,自動建立了消息隊列對象

MessageQueue

Looper.prepareMainLooper()

從名稱也很容易看出來,是直接在主線程内建立對象了。而在我們日常開發中,經常都是在主線程使用

Handler

,是以導緻了很少用到

Looper.prepare()

方法。

而生成

Looper

MessageQueue

對象後,則自動進入消息循環:

Looper.loop()

,我們不妨再看看裡面到底做了什麼?

截圖中的代碼比較簡單,大家應該不難看懂,我們再看看如何通過

MessageQueue.next()

來取消息設定阻塞狀态的。

我們取消息采用了一個無限 for 循環,當沒有消息的時候,則把标記位

nextPollTimeOutMillis

設定為 -1,在進行下一次循環的時候,通過

nativePollOnce()

直接讓其處于線程阻塞狀态。

再看看我們的消息分發是怎麼處理的,主要看上面的

msg.target.dispatchMessage(msg)

原來

msg.target

傳回的是一個

Handler

對象,我們直接看看

Handler.dipatchMessage(Message msg)

做了什麼。

總結:
  • 在主線程中

    Looper

    對象自動生成,無需手動生成。而在子線程中,一定要調用

    Looper.prepare()

    建立

    Looper

    對象。如果在子線程不手動建立,則無法生成

    Handler

    對象。
  • 分發消息給

    Handler

    的過程為:根據出隊消息的歸屬者,通過

    dispatchMessage(msg)

    進行分發,最終回調複寫的

    handleMessage(Message msg)

  • 在消息分發

    dispatchMessage(msg)

    方法中,會進行 1 次發送方式判斷:

    1. 若

    msg.callback

    屬性為空,則代表使用了

    post(Runnable r)

    發送消息,則直接回調

    Runnable

    對象裡面複寫的

    run()

    2. 若

    msg.callback

    屬性不為空,則代表使用了

    sendMessage(Message msg)

    發送消息,直接回調複寫的

    handleMessage(msg)

正常的消息 Message 是如何建立的?

我們經常會在

Handler

的使用中建立消息對象

Message

,建立方式也有兩個

new Message()

或者

Message.obtain()

。我們通常都更青睐于

Message.obtain()

這種方式,因為這樣的方式,可以有效避免重複建立

Message

對象。實際上在代碼中也是顯而易見的。

Handler 的另外一種使用方式

前面主要講解了

Handler.sendMessage(Message msg)

這種正常使用方式,實際上,我們有時候也會用

Handler.post(Runnable r)

進行處理,我們當然應該看看裡面是怎麼處理的。

從官方注釋可以看到,這會直接将

Runnable

對象加到消息隊列中,我們來看看 `getPostMessage(r) 到底做了什麼。

我們上面的分析是對的。在

getPostMessage(Runnable r)

方法中,我們除了通過

Message.obtain()

方法來建立消息對象外,專門把

Runnable

對象指派給了

callback

,這樣才用了上面做消息分發的時候,通過這個标記來判斷是用的

post()

還是

sendMessage()

方式。

到底是如何發消息的?

一直在說通過

sendMessage()

方式來發消息,到底這個消息是怎麼發送的呢?

直接看

sendMessageAtTime()

enqueueMessage()

裡面做了什麼?

至此,你大概明白了兩種方式的差別了。

寫在最後

本次内容可能講的比較多和亂,還望大家跟着到源碼中一步一步分析,最困難的時候,就是提升最大的時候!

繼續閱讀