難點:html
next()
方法,enqueueMessage()
方法,由於它們與 Native 層的 Looper 和 MQ 關聯。重點:java
Handler 機制中有 4 個主要的對象:Handler、Message、MessageQueue 和 Looper. Handler 負責消息的發送和處理;Message 是消息對象,相似於鏈表的一個結點;MessageQueue 是消息隊列,用於存放消息對象的數據結構;Looper 是消息隊列的處理者(用於輪詢消息隊列的消息對象,取出後回調 handler 的 dispatchMessage()
進行消息的分發,dispatchMessage()
方法會回調 handleMessage()
方法把消息傳入,由 Handler 的實現類來處理。)git
當咱們在某個線程當中調用 new Handler()
的時候會使用當前線程的 Looper 建立 Handler. 當前線程的 Looper 存在於線程局部變量 ThreadLocal 中。在使用 Handler 以前咱們須要先調用 Looper.prepare()
方法實例化當前線程的 Looper,並將其放置到當前線程的線程局部變量中(只放一次,之後會先從 TL 中獲取再使用,此時會調用 Looper 的構造方法,並在構造方法中初始化 MQ),而後調用 Looper.loop()
開啓消息循環。主線程也是同樣,只是主線程的 Looper 在 ActivityThread 的 main()
方法中被實例化。咱們可使用 Looper.getMainLooper()
方法來獲取主線程的 Looper,並使用它來建立 Handler,這樣咱們就能夠在任何線程中向主線程發送消息了。github
Looper.prepare(); // 內部會調用 Looper 的 new 方法實例化 Looper 並將其放進 TL
new Handler().post(() -> /* do something */);
Looper.loop();
複製代碼
當實例化 Looper 的時候會同時實例化一個 MessageQueue,而 MessageQueue 同時又會調用 Native 層的方法在 Native 層實例化一個 MessageQueue 還有 Looper. Java 層的 Looper 和 Native 層的 Looper 之間使用 epoll 進行通訊。當調用 Looper 的 loop()
方法的時候會啓動一個循環來對消息進行處理。Java 層的 MQ 中沒有消息的時候,Native 層的 Looper 會使其進入睡眠狀態,當有消息到來的時候再將其喚醒起來處理消息,以節省 CPU.面試
在 Looper 的 loop()
中開啓無限循環爲何不會致使主線程 ANR 呢?這是由於 Android 系統自己就是基於消息機制的,所謂的消息就是指發送到主線程當中的消息。之因此產生 ANR 並非由於主線程當中的任務無限循環,而是由於無限循環致使其餘的事件得不處處理。安全
《Android 消息機制:Handler、MessageQueue 和 Looper》數據結構
handler內存泄漏及解決辦法:若是 Handler 不是靜態內部類,Handler 會持有 Activity 的匿名引用。當 Activity 要被回收時,由於 Handler 在作耗時操做沒有被釋放,Handler Activity 的引用不能被釋放致使 Activity 沒有被回收停留在內存中形成內存泄露。多線程
解決方法是:1). 將 Handler 設爲靜態內部類;2). 使 Handler 持有 Activity 的弱引用;3). 在 Activity 生命週期 onDestroy()
中調用 Handler.removeCallback()
方法。異步
Android 中的控件不是線程安全的,之因此這樣設計是爲了:1).設計成同步的能夠簡化使用的複雜度;2).能夠提高控件的性能(異步加鎖在非多線程環境是額外的開銷)。oop
post()
方法時 Handler 所在的線程)post()
方法所在的線程由 Looper 所在線程決定的;最終邏輯是在 Looper.loop()
方法中,從 MQ 中拿出 Message,而且執行其邏輯。這是在 Looper 中執行的。所以由 Looper 所在線程決定。
不論你調用 send()
類型的方法仍是 post()
類型的方法,最終都會調用到 sendMessageAtTime()
方法。post()
和 postDelay()
的區別在於,前者使用當前時間,後者使用當前時間+delay 的時間來決定消息觸發的時間。最終方法的參數都將被包裝成一個 Message 對象加入到 Handler 對應的 Looper 的 MQ 中被執行。
Looper 和 Handler 不須要再一個線程中,默認的狀況下會從 TL 中取當前線程對應的 Looper,但咱們能夠經過顯式地指定一個 Looper 的方式來建立 Handler. 好比,當咱們想要在子線程中發送消息到主線程中,那麼咱們能夠
Handler handler = new Handler(Looper.getMainLooper());
複製代碼
用戶層面發送的都是同步消息,不能發送異步消息;異步消息只能由系統發送。
調用 MessageQueue.next()
方法的時候會調用 Native 層的 nativePollOnce()
方法進行精準時間的阻塞。在 Native 層,將進入 pullInner()
方法,使用 epoll_wait
阻塞等待以讀取管道的通知。若是沒有從 Native 層獲得消息,那麼這個方法就不會返回。此時主線程會釋放 CPU 資源進入休眠狀態。
當咱們加入消息的時候,會調用 MessageQueue.enqueueMessage()
方法,添加完 Message 後,若是消息隊列被阻塞,則會調用 Native 層的 nativeWake()
方法去喚醒。它經過向管道中寫入一個消息,結束上述阻塞,觸發上面提到的 nativePollOnce()
方法返回,好讓加入的 Message 獲得分發處理。
MessageQueue.enqueueMessage()
使用 synchronized 代碼塊去進行同步。
資料:Android 中的 Handler 的 Native 層研究
dispatchMessage()
分發消息的處理流程?使用 Handler 的時候咱們會覆寫 Handler 的 handleMessage()
方法。當咱們調用該 Handler 的 send()
或者 post()
發送一個消息的時候,發送的信息會被包裝成 Message,而且將該 Message 的 target 指向當前 Handler,這個消息會被放進 Looper 的 MQ 中。而後在 Looper 的循環中,取出這個 Message,並調用它的 target Handler,也就是咱們定義的 Handler 的 dispatchMessage()
方法處理消息,此時會調用到 Handler 的 handleMessage()
方法處理消息,並回調 Callback.
當 Handler 在消息隊列中被執行的時候會直接調用 Handler 的 dispatchMessage()
方法回調 Callback.
Looper.myLooper()
是如何獲取到當前線程的 Looper 的?從 TL 中獲取
是單鏈表,不是隊列
quit()
和 quitSafely()
的本質就是讓消息隊列的 next()
返回 null,以此來退出Looper.loop()
。
quit()
調用後直接終止 Looper,不在處理任何 Message,全部嘗試把 Message 放進消息隊列的操做都會失敗,好比 Handler.sendMessage()
會返回 false,可是存在不安全性,由於有可能有 Message 還在消息隊列中沒來的及處理就終止 Looper 了。
quitSafely()
調用後會在全部消息都處理後再終止 Looper,全部嘗試把 Message 放進消息隊列的操做也都會失敗。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
void quit(boolean safe) {
if (!mQuitAllowed) throw new IllegalStateException("Main thread not allowed to quit.");
synchronized (this) {
if (mQuitting) return;
mQuitting = true;
if (safe) removeAllFutureMessagesLocked(); // 把全部延遲消息清除
else removeAllMessagesLocked(); // 直接把消息隊列裏面的消息清空
nativeWake(mPtr);
}
}
複製代碼
1).next()
方法返回的 msg == null;2).線程意外終止。
next()
方法;msg.target.dispatchMessage(msg)
進行消息分發。Looper.loop()
方法執行時,若是內部的 myLooper()
獲取不到Looper會出現什麼結果?異常
經過保證只有一個 Looper 來保證只有以一個 MQ. 在一個線程中使用 Handler 以前須要使用 Looper.prepare()
建立 Looper,它會從 TL 中獲取,若是發現 TL 中已經存在 Looper,就拋異常。
根據消息的分發機制,Looper 不會區分 Handler,每一個 Handler 會被添加到 Message 的 target 字段上面,Looper 經過調用 Message.target.handleMessage()
來讓 Handler 處理消息。
關注做者,及時獲取更多高級面試題解 :)
Android 高級面試系列文章,關注做者及時獲取更多面試資料,
本系列以及其餘系列的文章均維護在 Github 上面:Github / Android-notes,歡迎 Star & Fork. 若是你喜歡這篇文章,願意支持做者的工做,請爲這篇文章點個贊👍!