說道 Android 的消息機制,其實說的就是 Handler 的運行機制以及 Handler 所附帶的 MessageQueue 和 Looper 的工程過程。
一、 Handler 的運行機制
當 Handler 創建的時候,會採用當前線程的 Looper 來構建消息循環系統,如果當前線程沒有 Looper 則會報錯,當然,開始是 UI 線程,所以不用擔心。
當然,當Looper 被創建的時候, MessageQueue 也被創建好了,這樣 Looper 和 MessageQueue 就可以跟 Handler 一起工作了。
這裏,先做一下 Handler 的工作流程:
當我們使用 Handler 的send 或者 post 的時候,它會調用 MessageQueue 的 qnqueueMessage 方法,將這個 消息放到消息隊列中,然後 Looper 發現有新消息到來時,就會處理這個消息了;然後 Handler 的 handleMessage 方法就會被調用。
注意 Looper 是運行在創建 Handler 所在的線程中的,這樣一來,Handler 中的業務邏輯就會被切換到創建 Handler 所在的縣城中去執行了。
過程可以用下圖表示(圖片來源):
接下來繼續深入它。
1.2、消息隊列的工作原理
消息隊列在 Android 中指的是 MessageQueue,它其實不是隊列,裏面實現的單鏈表結構,它主要包含兩個操作:插入和讀取。
插入對應 enqueueMessage ,讀取則對應 next,其中 enqueueMessage 表示往隊列中插入一條消息,而 next 則從隊列中取出消息,並將消息從隊列中刪除。
其中,需要注意的是它的 next 方法:
Message next() {
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
...
}
...
可以發現 next 方法是一個無線循環的方法,如果消息隊列沒有消息,那麼next 方法會一直阻塞在這裏。當有新消息到來時,next 方法會返回這條消息並將它從單鏈表中刪除。
1.3 Looper 的工作原理
Looper 在 Android 的消息機制中扮演者消息隊列的角色,具體來說,你會不停地從 MessageQueue 中查看是否有新的消息,如果有則取出消息,如果沒有,則一直阻塞在那裏。
在 Looper 的構造方法中,會創建一個 MessageQueue ,然後將當前線程保存起來。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
然後,Handler 的創建是需要 Looper 的,沒有Looper 是會報錯的,那 Looper 如何創建呢?
很簡單,在線程中,通過 Looper.prepare() 爲線程開啓一個 Looper,接着使用 Looper.loop() 來開啓消息循環模式,如下所示:
new Thread(){
@Override
public void run() {
Looper.prepare();
super.run();
Looper.loop();
}
};
當然,這個是普通線程的,如果是 UI 線程,還提供了 prepareMainLooper 方法,這個方法主要是給 ActivityThread 創建Looper 使用,由於主線程比較特殊,也可以使用 Looper.getMainLooper() 來獲取主線程Looper。
比如常用在共用類中:
public static Handler HANDLER = new Handler(Looper.getMainLooper());
1.3.1 Looper 的退出
Looper 也是可以退出的,有 quit() 和 quitSafely() 兩個方法;quit() 會直接退出,二 quitSalely() 則是已有消息都處理完畢,纔會退出。
如果是自己定義的 Looper ,則建議要執行退出操作,否則這個子線程就會一直處於等待狀態。容易造成內存泄漏,當然主線程就不用我們操心了。
Looper 的重要方法是 loop 方法,只有調用了 loop 後,消息循環系統纔會真正起作用,它的實現如下:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
...
boolean slowDeliveryDetected = false;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
...
可以看到,只有 queue.next() 爲null 時,纔會退出這個循環;當執行了 quit 或者 quitSafely 來通知 Looper 退出,它就會調用 MessageQueue 來退出,這樣 queue.next() 就會返回null,looper 也就退出了。
否則就會一直阻塞在這裏,一直等到 next 有新的消息,則 msg.target.dispatchMessage(msg); 就會被執行,這樣 Handler 的dispatchMessage 就會被執行了。可以看到 Handler 的 dispatchMessage 方法如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先,先檢查 msg.callback 是否爲null,Message 的callback 是一個 RUnnable 對象,實際上就是 Hnadler 的post 方法所傳遞的 Runnable 。所以,如果我們使用 了 Handler.post ,則不會走 handleMessage(msg) ;
如果不是,則檢查 mCallback 是否爲null,mCalback 其實就是個接口:
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
如果不爲空,最終就會調用到我們熟悉的 handleMessage(msg) 方法了。
這樣,Handler 的消息機制就分析完了。
這裏,面試官就會問了,既然 looper 這裏是個無線循環,爲啥不會阻塞UI線程?
爲了回答這個問題,首先,我們先去到主線程的消息隊列
二、主線程的消息隊列
Android 的主線程就是 ActivityThread,主線程的入口爲 main 方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
....
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到,main() 方法會通過 Looper.prepareMainLooper() 來創建 Looper 和 MessageQueue,然後創建了 ActivityThread線程,最後通過 Looper.loop() 開啓循環。
而它的 Handler 就是 ActivityThread.H ,裏面對應着 Activity 的啓動等消息:
那這還是不能說明UI線程爲啥不會阻塞啊?!!
別急,除了這個 ActivityThread 這個線程,其實還有個 ApplicationThread 線程,它裏面創建了 Binder 方法,當 ApplicationThread 與 AMS 進行進程間通信完成後,就會通過 ApplicationThread 會向 H 發送消息,H 收到消息之後,就會將 ApplicationThread 中的邏輯切換到 ActivityThread 中取執行,即切換到主線程去執行了。
比如 Activity 的啓動最後就通過 ApplicationThread 發送 LUANCHER_ACTIVITY 給 H 去啓動的。
所以,UI線程並不會因爲looper而阻塞,但如果我們主線程中做了一些耗時操作,導致導致 MessageQueue 的消息過多,等待執行,造成 ANR。
自此,我們的 Android 消息機制就分析完了。
參考 Android 藝術開發 第10章