關於 Handler 還應該問問本身什麼 | 快問快答

1.一個線程有幾個 Handler?

答:一個線程能夠用有多 Handler,由於 Handler 最終是被 Message 持用的(post 裏面的 Runnable 最終也會被包裝成一個 Message),以便 Looper 在拿到 Message 後調用 Handler 的 dispatchMessage 完成回調,並且項目中仔細去看也確實如此,咱們能夠每一個 Activity 中都建立一個 Handler 來處理回調到主線程的任務。java

2.一個線程有幾個 Looper?如何保證?

答:一個線程只能擁有一個 Looper,這裏從源碼中就能夠看到,sThreadLocal.set 只調用了一次,若是再次調用 prepare 會判斷 sThreadLocal.get 是否爲空,若是不爲空就直接拋出異常了,也就是同一線程屢次調用 prepare 方法會直接崩潰,這裏也是避免了程序去修改某個線程已經設置好的 Looper 值。 android

image.png

補充:ThreadLocal 提供線程局部變量,也就是對應值只有該線程支持,並不會多線程共享。那它是如何作到線程局部變量這個效果的呢?它內部靠的是 ThreadLocalMap,線程做爲 key,值做爲 value,這樣我去取對應值的時候,其實經過線程 Key 拿去對應的 value,這樣就保證了值是當前線程獨享的。面試

3.爲什麼主線程可使用 Handler?若是想要在子線程中使用 Handler 機制要作些什麼準備?

答:先來看下面的源碼,Handler 的構造中(不管調用哪一個最終都會走到這裏),是須要判斷當前線程是否存在 Looper 的,若是不存在會直接拋出異常,主線程之因此可使用 Handler 是由於系統幫在 ActivityThread 中已經幫咱們建立了 Looper 而且已經讓它運行了起來。安全

image.png

系統幫咱們在主線程建立 Looper 的代碼:bash

image.png

若是咱們如今子線程中使用 Handler 的話,之須要模仿系統怎麼建立 Looper 便可,其實就是兩步,在子線程中調用 Looper.prepare() 和 Looper.loop() 便可,prepare 幫咱們在對應線程建立 Looper,loop 讓剛剛建立好的 Looper 運行起來。數據結構

這兩步完成後咱們就能夠在子線程中使用 Handler 了。多線程

以上所說的 Handler 使用指的是 Handler 的建立,好比在 A 線程建立後就能夠在任何位置使用了,也就是在任意線程發送消息,而後在 A 線程處理消息。app

4.既然能夠存在多個 Handler 往 MessageQueue 中添加數據(發消息時各個 Handler 可能處於不一樣線程),那它內部是如何確保線程安全的?

答:這裏主要關注 MessageQueue 的消息存取便可,看源碼內部的話,在往消息隊列裏面存儲消息時,會拿當前的 MessageQueue 對象做爲鎖對象,這樣經過加鎖就能夠確保操做的原子性和可見性了。oop

消息的讀取也是同理,也會拿當前的 MessageQueue 對象做爲鎖對象,來保證多線程讀寫的一個安全性。 post

image.png

5.咱們使用 Message 時應該如何建立它?

答:建立的它的方式有兩種,一種是直接 new 一個 Message 對象,另外一種是經過調用 Message.obtain() 的方式去複用一個已經被回收的 Message,固然平常使用者是推薦使用後者來拿到一個 Message,由於不斷的去建立新對象的話,可能會致使垃圾回收區域中新生代被佔滿,從而觸發 GC。

Message 中的 sPool 就是用來存放被回收的 Message,當咱們調用 obtain 後,會先查看是否有可複用的對象,若是真的沒有才會去建立一個新的 Message 對象。

補充:主要的 Message 回收時機是:

  • 在 MQ 中 remove Message 後;
  • 單次 loop 結束後;
  • 咱們主動調用 Message 的 recycle 方法後;

6.Message 的數據結構是什麼樣子?

答:單鏈表,Message 中會經過 next 來持有下一個 Message 對象的引用,這是一個典型的鏈表結構。

image.png

其實文中寫到的這些問題並不僅是問到這裏就結束了,不少問題均可以深挖,尤爲是這個問題,看着很短,接下去問不少關於鏈表的其餘問題了,好比鏈表反轉,或是延伸到其餘數據結構問題。

7.主線程 Looper 與子線程 Looper 有什麼不一樣?

答:最主要的區別還在在於 Looper 的 loop 循環是否可以退出,主線程建立時傳入的 quitAllowed 是 false。

Looper.loop 這個方法在拿到的消息爲空時就會退出那個死循環,不過通常是不爲空的,哪怕沒有消息最多也是阻塞,只有調用 Looper.quit 時纔會在消息隊列清空消息並把消息設置爲 null。

那 loop 的死循環結束意味着什麼呢?看下面 ActivityThread 的代碼就知道了,若是主線程 Looper 結束就說明程序也要退出了,由於只有 loop 不斷執行纔不會走到拋出異常那一行。

文件:android/app/ActivityThread.java
    
    public static void main(String[] args) {
        //  ...
        //  主線程 Looper 建立
        Looper.prepareMainLooper();
        //  ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        //  ...
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
複製代碼

最後的最後

以上是我問題是我最近去面試過程當中遇到的一些關於 Handler 問題,整理出來強化記憶。但願對你也有幫助。

若是你以爲我寫的還不錯的話,那就經過點贊,點贊,還 tm 是點讚的方式給我反饋吧,感謝你的支持。

相關文章
相關標籤/搜索