轉載請註明出處:juejin.im/post/5c9896…面試
以前在郭神的訂閱號,看到了這一篇關於Handler
的投稿文章bash
介紹得很詳細,分析源碼的流程也很清晰。ide
從 Message
的對象獲取方式,到 Handler
的 sendMessage
方法解析,再到 enqueueMessage
方法解析。函數
在 enqueueMessage
中沒有看到回調方法 handleMessage
,結果是 dispatchMessage
方法調用了 handleMessage
。oop
再看 Handler
和 Looper
類的關係,又發現 loop
方法中調用了 dispatchMessage
,最後分析了 loop
方法,得出結論,MessageQueue
消息隊列最後是在這個方法執行的。post
這位做者大佬也是事無鉅細,在重要的方法和代碼處都加了註釋,使得讀者理解起來更輕鬆,值得學習。學習
但因爲我的理解能力有限,部分地方還存在疑惑,因而就本身動手豐衣足食,結合面試中可能會問到的一些問題來作了學習。ui
因而有了此文。spa
面試官最愛問的一個問題
首先咱們瞭解一下
Handler
—— 處理者
Message
—— 消息
MessageQueue
—— 消息隊列
Looper
—— 循環者
因而,咱們大概能夠這樣描述:
消息處理者 Handler
從子線程中發送消息 Meesage
到 消息隊列 MessageQueue
中,消息隊列 MessageQueue
會對消息進行排序,循環者 Looper
循環地從消息隊列 MessageQueue
中取出消息,回調給主線程中的消息處理者 Handler
, 在 handleMessage
方法處理結果。完成一次消息異步發送的流程。
而後,走一遍流程:
public Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//TODO..
}
}
複製代碼
handler.sendMessage(m);
handler.post(r);
複製代碼
queue.enqueueMessage(msg, uptimeMillis);
複製代碼
MessageQueue
中入隊方法 enqueueMessage()
,出隊方法 next()
boolean enqueueMessage(Message msg, long when) {...
Message next() {...
複製代碼
next()
在循環者 Looper
的 loop
方法中被調用
獲取到消息後,調用 dispatchMessage(msg)
來分發消息
public static void loop() {...
msg.target.dispatchMessage(msg);
...
複製代碼
最終回到了 Handler 類中的 dispatchMessage
方法,若是 Message
是一個純粹的消息體,那將會調用 handleMessage
方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
到此,走完了此次流程。
我想你大概能知道,如何組織本身的語言回答面試官了。
但,可能多數面試官不止於此。
繼續問道...
這個問題,不太難理解,Ctrl+鼠標左鍵,看過源碼的都能知道。咱們跟着源碼走一遍
handler.post(new Runnable() {...
複製代碼
這裏實際調用了 sendMessageDelayed(Message msg, long delayMillis)
將 Runnable
轉成了 Message
對象,如何轉換的呢
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
複製代碼
能夠看到 Message
中的 callback
屬性是一個 Runnable
對象
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
複製代碼
這個問題自己多是有問題的...
loop
方法中爲何要用死循環?先說 ActivityThread
,雖然類名以Thread
結尾,但它並無繼承 Thread
,它並非一個線程類。
ActivityThread
是Android
應用程序的入口,也就是任何一個進程的主線程入口。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
複製代碼
這就是一個Java 應用,main
函數是入口,當執行到 loop
方法就開始死循環,若是死循環結束,那將會報錯。
能夠看到就是這樣設計的。目的就是爲了咱們點擊應用的圖標時不會一閃而過,而是正確地打開頁面,執行生命週期,響應各類輸入事件。
咱們的代碼就是在主線程 loop
函數中的死循環中被執行的,因此這和 ANR 是兩個不一樣的東西。
至於 Looper
會被阻塞,這個更深刻一些,不在這裏討論。
1.輸入事件(按鍵和觸摸事件)5s內沒被處理。
2.BroadcastReceiver
的事件(onRecieve方法)在規定時間內沒處理完(前臺廣播爲10s,後臺廣播爲60s)。
3.service
前臺20s後臺200s未完成啓動。
4.ContentProvider
的publish
在10s內沒進行完。
因此它不包括 Looper.loop()
的死循環。
一直想總結一下本身對 Handler
的認識,以加強記憶,幫助學習和理解。
直到今天才完成。
關於 Handler
,它實現了線程之間的通訊,而線程只是CPU調度的最小單位。瞭解了 Handler
的原理只是看到冰山一角。要想了解更多的通訊方式,還有很長的路要走。
Linux已經擁有的進程間通訊IPC手段包括(Internet Process Connection): 管道(Pipe)、信號(Signal)和跟蹤(Trace)、插口(Socket)、報文隊列(Message)、共享內存(Share Memory)和信號量(Semaphore)。而 Binder 是主要的IPC方式。
記錄在此,僅爲學習!
感謝您的閱讀!歡迎指正!
參考: