轉載請注明出處:juejin.im/post/5c9896…
寫在前面
之前在郭神的訂閱号,看到了這一篇關于
Handler
的投稿文章
《或許你可以從這個角度去了解Handler》
介紹得很詳細,分析源碼的流程也很清晰。
從
Message
的對象擷取方式,到
Handler
的
sendMessage
方法解析,再到
enqueueMessage
方法解析。
在
enqueueMessage
中沒有看到回調方法
handleMessage
,結果是
dispatchMessage
方法調用了
handleMessage
。
再看
Handler
和
Looper
類的關系,又發現
loop
方法中調用了
dispatchMessage
,最後分析了
loop
方法,得出結論,
MessageQueue
消息隊列最後是在這個方法執行的。
這位作者大佬也是事無巨細,在重要的方法和代碼處都加了注釋,使得讀者了解起來更輕松,值得學習。
但由于個人了解能力有限,部分地方還存在疑惑,于是就自己動手豐衣足食,結合面試中可能會問到的一些問題來做了學習。
于是有了此文。
這是重點
1.Handler 的原理?
面試官最愛問的一個問題
首先我們了解一下
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);
}
}
複制代碼
到此,走完了這次流程。
我想你大概能知道,如何組織自己的語言回答面試官了。
但,可能多數面試官不止于此。
繼續問道...
2.Handler 為什麼可以 post Runnable?
這個問題,不太難了解,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;
}
複制代碼
3.Loop 死循環為什麼不會導緻主線程 ANR?
這個問題本身可能是有問題的...
- 首先我們要知道
方法中為什麼要用死循環?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
會被阻塞,這個更深入一些,不在這裡讨論。
- 什麼情況下會發生ANR?
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方式。
記錄在此,僅為學習!
感謝您的閱讀!歡迎指正!
參考:
1.yanzhenjie.blog.csdn.net/article/det…
2.www.zhihu.com/question/34…
3.www.jianshu.com/p/fa962a5fd…
轉載于:https://juejin.im/post/5c9896ca6fb9a070f30b0e18