前言:很久之前就想寫關于 Handler 消息機制的文章分析了,但是感覺內容比較多不太容易講的明白,就暫時告了一段落了。現在開始寫源碼分析的文章,發現很多地方都用到了 Handler 這個東西,還是躲不過去啊,畢竟是安卓系統里面非常重要的一個內容。所以今天我就分析分析這安卓消息機制到底是如何運轉的。
我將 Handler 消息機制的流程總結為五個階段,這里以主線程的消息機制為例:
系統創建(創建主線程的 Looper、MessageQueue,并開啟消息循環)
創建 Handler
創建 Message
通過 Handler 發送 Message
處理 Message
注:子線程的消息機制其實和主線程一樣,不同的就是主線程第一步系統幫你做了,而子線程要自己手動去創建 Looper 并開啟消息循環。
接著我會從上面的五個階段一個個分析過來。相信每個安卓開發都使用過 Handler,不過為了大家方便對照理解,我先給出 Handler 最普遍的使用例子,然后利用這個例子來對上面的五個階段進行源碼分析。
public class MainActivity extends AppCompatActivity {
Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1.系統創建(創建主線程 Looper、主線程 MessageQueue)
// 這一步不需要我們進行任何操作,因為系統自動幫我做了
// 2.創建 Handler
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO
// 5.處理 Message
}
};
new Thread() {
@Override
public void run() {
// 3.創建 Message
Message message = new Message();
message.what = 1;
// 4.利用 Handler 發送 Message
mHandler.sendMessage(message);
}
}.start();
}
}
1、系統創建
在我們運行一個 app 的最開始,安卓系統會進入到 ActivityThread 這個類中的靜態 main 方法,學過 java 的同學應該知道,這就是一個應用程序的入口,所以我們的應用也是從這里開始的。
public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
...// 僅貼出關鍵代碼
Looper.prepareMainLooper();
Looper.loop();
}
}
可以看到第七行,這里使用了 Looper.prepareMainLooper(),就是這一句話完成了主線程 Looper、MessageQueue 的創建。貼出它的代碼看看:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
看到第二行,這里使用了 prepare(false) 去初始化 Looper 和 MessageQueue。然后再第七行,使用 myLooper() 獲取剛剛創建完成的 Looper。具體如下所示:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
看到第二行,這里先利用 sThreadLocal.get() != null 去判斷這個線程是否已經擁有一個 Looper。這里的 sThreadLocal 是 ThreadLocal 類的一個對象,ThreadLocal 是一個范型類,這里的范型就是 Looper,通過調用它的 get 方法能夠獲得 Looper 對象。后面使用 myLooper() 去獲取 Looper 也是通過 sThreadLocal.get() 方法。具體 ThreadLocal 的工作原理可以參考 Android的消息機制之ThreadLocal的工作原理。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,這里先獲取了當前線程的 Looper 和 MessageQueue。然后就是一個 for 循環,在第十六行使用 queue.next() 去 MessageQueue 中不斷取消息。由于 queue.next() 也是一個循環,所以當沒有消息時,這一步就會阻塞,直到取出消息。因為我們這里是主線程,所以這個 MessageQueue 永遠不會返回 null,也就不會 msg ==null 這個判斷 return 了。拿到消息,接著就把消息分發給 Handler 處理。最后在第二十七行回收這個消息。
我們先看看 MessageQueue 的 next 方法做了什么:
public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
...// 僅貼出關鍵代碼
// 這一步已經分析過了(創建了 Looper、MessageQueue)
Looper.prepareMainLooper();
// 開啟消息循環
Looper.loop();
}
}
再創建了主線程 Looper 和 MessageQueue 之后,系統就使用 Looper.loop() 開啟消息循環了,如下所示:
public static void loop() {
... // 僅貼出關鍵代碼
// 獲取當前線程的 Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 獲取當前線程的 MessageQueue
final MessageQueue queue = me.mQueue;
for (;;) {
// 從 MessageQueque 中取消息
// 由于 MessageQueque.next() 也是一個循環,所以如果沒有消息就阻塞了
Message msg = queue.next();
// 主線程這里的消息永遠不會為 null,因為 MessageQueue 不會被 quit
if (msg == null) {
return;
}
// 消息的分發
msg.target.dispatchMessage(msg);
// 消息的回收
msg.recycleUnchecked();
}
}
可以看到,在第六行和第十一行分別獲取了當前線程的 Looper 和 MessageQueue。然后就是一個 for 循環,在第十六行使用 queue.next() 去 MessageQueue 中不斷取消息。由于 queue.next() 也是一個循環,所以當沒有消息時,這一步就會阻塞,直到取出消息。因為我們這里是主線程,所以這個 MessageQueue 永遠不會返回 null。然后在第二十四行的消息分發給 Handler 處理。最后在第二十七行回收這個消息。
我們先看看 MessageQueue 的 next 方法做了什么:
Message next() {
...// 僅貼出關鍵代碼
for (;;) {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
// 如果還沒到處理消息的時候,就繼續循環
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 把這個消息取出來返回給 looper 進行處理
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
// 如果使用了 quit,這里就會返回 null,然后當前線程的 looper 循環也就結束了
if (mQuitting) {
dispose();
return null;
}
}
}
如上代碼所示,我在關鍵的地方都已經標出注釋了,然后再稍微解釋一下。首先這個 next() 方法是一個循環,它會不斷的從消息隊列中取消息。如果消息隊列中的第一個消息不為 null,并且到了處理這個消息的時間,那么就把這個消息移出消息隊列,返回給 looper 進行處理。否則就一直進行循環直到取出消息或者使用了 quit() 方法停止了當前 looper 循環。
接著我們回到 looper 方法,看看這個 next() 返回消息之后,是怎么進行消息分發的。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 1.使用 post 方法發送消息,會在這里執行它的 run 方法
handleCallback(msg);
} else {
if (mCallback != null) {
// 2.使用 Handler(new Handler.Callback()) 創建的 handler 對象,
// 會在它的回調函數 handleMessage 內處理消息
if (mCallback.handleMessage(msg)) {
return;
}
}
// 3.使用 Handler() 創建的對象通過 sendMessage 方法發送消息,會在 handler 的
// handleMessage 方法內處理消息
handleMessage(msg);
}
}
可以看到,這里有三種方式去處理消息,而且是有優先級的,具體哪一種取決于你創建 Handler 對象的姿勢和發送 Message 的姿勢,這個相信大家都清楚,我上面也給出注釋了,就不再解釋了。
總結一下,在我們 app 啟動的入口,系統會自動幫我們初始化主線程 Looper,并開啟 Looper 循環,它會不斷的從 MessageQueue 中取出消息,然后通過分發消息去處理消息。并且主線程的 Looper 是無法停止的,而子線程的 Looper 可以通過調用 quit 方法去停止循環的,子線程的消息分發機制與主線程不同之處就是需要手動去創建 Looper 并開啟 Looper 循環。
2、創建 Handler
在最上面給出的例子中,我們使用 new Handler() 來創建了一個 Handler 對象,讓我們看看它的構造方法:
// 這是我們調用的構造方法
public Handler() {
this(null, false);
}
// 最終會調用到這個構造方法
public Handler(Callback callback, boolean async) {
... // 僅貼出關鍵代碼
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
可以看到,在我們使用這個沒有參數的構造方法之后,它又調用帶兩個參數的構造方法。在第八行,先獲取到了當前線程的 Looper,然后在第九行判斷這個 Looper 是否存在,由于我們主線程的 Looper 系統已經創建了,所以這是判斷為 false,繼續往下,在第十四行獲取當前線程的 MessageQueue。
總結一下,在我們調用 Handler 的無參構造方法時,這個 handler 對象就會綁定當前線程的 Looper 和 MessagQueue。
如果你要在子線程中使用 Handler 時,要先使用 Looper.prepare() 去初始化子線程的 Looper,否則會拋出異常。還有記得要使用 Looper.loop() 去開啟消息循環,不然你的子線程 Handler 是收不到消息的。
3、創建 Message
在示例中我們通過 new Message() 的方式創建了一個 Mesage,然后可以為這個 Message 的 waht 賦值來進行不同消息的區分,我們還可以通過為它的 obj 賦值來傳遞 Object 對象。
當然了,有一種更好的方式來創建 Message,通過使用 Message.obtain() 來復用之前的 Message,以此來減少創建一個新的 Message 帶來的系統開銷。
總結一下,通過 new Message() 或者 Message.obtain() 來獲得一個 Message,然后可以為 Message 設置各種參數來傳遞給 Handler。
4、發送 Message
在示例中,我們使用 Handler.sendMessage(message) 去發送消息,最終它會調用 Handler 的 enqueueMessage 方法,將消息存入消息隊列,如下所示:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,第二行的 msg.target 就是我們主線程創建的 mHandler,第六行通過 queue.enqueueMessage 將我們創建的消息存入了消息隊列。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (mQuitting) {
// 如果已經調用過了 quit,這里就會拋出異常
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
// mMessages 可以理解為消息隊列的第一個消息
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 如果消息隊列中沒有消息或者這個消息的執行時間要被隊列首消息的執行時間還要早
// 就把這個消息插入到消息隊列的頭部
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 否則就從消息隊列的頭部往后排,直到后面沒有消息了或者此消息的執行時間比那個消息的執
// 行時間早,就插入到那個消息之前
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
}
return true;
}
可以看到,這個方法就是消息的入隊方法,通過比較各個消息的執行時間,來進行排序從小到大的排序,執行時間在前的就在隊列的前面。這里的算法是單鏈表的插入算法,大家可以通過自己畫圖模擬來理解。
另外,我們除了使用 Handler.sendMessage(message) 方法去發送消息外,還可以使用 Handler.post(new Runnable()) 去發送消息。這里其實就是把這個 Runnable 對象組裝成一個 Message 去存入消息隊列,最后在分發事件的時候,不再通過 handleMessage 處理,而是執行它的 run() 回調方法去處理罷了,對我們理解消息通信機制來說沒影響。
總結一下,通過調用 Handler.sendMessage(message) 或者 Handler.post(new Runnable()) 方法,可以把一個消息從子線程傳遞到 Handler 所在線程的 MessageQueue,也就是這一步通過 Handler 實現了線程的切換。
5、處理 Message
在示例中,我們使用 handleMessage(Message msg) 來處理消息,這是最后一步了,沒有具體源碼分析,在第一個流程系統創建 Looper 中里,我們講到了 looper 中會使用 dispatchMessage 方法取分發消息。
那么讓我們回顧一下它是怎么收到消息的。首先我們通過創建 Message,然后用主線程的 Handler 發送這個消息到 MessageQueue,然后 Looper 循環不斷的從 MessageQueue 中取消息,取到消息之后就通過消息分發,將這個 Message 作為參數傳遞給 handleMessage 進行處理。
全文總結
我們來回顧一下 Handler 消息機制的幾個流程,并給出需要注意的地方:
- 創建 Looper 并開啟消息循環
- 主線程不需要此步驟,因為系統自動創建并開啟了 Looper。
- 子線程需要手動調用 Looper.prepare() 去創建 Looper,并調用 Looper.loop() 開啟消息循環。
- 創建 Handler 對象
- 若想要在主線程接收消息,這個 Handler 對象就需要在主線程創建。
- 若想在子線程接收消息,就需要在子線程創建 Handler 對象,記得在創建 Handler 對象之前創建 Looper,否賊就會拋出異常。
- 創建 Message
- 可以使用 Message.obtain() 來獲取 Message 對象,來減少系統創建對象的開銷。
- 發送 Message
- 可以使用 sendMessage 系列方法去發送消息,也可以使用 post 系列方法去發送消息。post 系列方法最終也是通過 sendMessage 系列方法去實現消息入隊的。不同之處在于處理方法的回調不同。
- 處理 Message
- 如果是通過 sendMessage 系列方法發送的消息,最終會在 handleMessage 中處理。如果是通過 post 系統方法發送的消息,最終會在 run 回調中處理。
最后,這篇文章分析 Handler 消息機制的文章也算結束了。文章在源碼分析的一些小細節上沒有很深入,因為我覺得如果每個細節都貼出來的話,文章比較容易偏離主題,看了細節代碼再回到主線去講解,讓讀者分不清主次。然后文章也是我站在巨人的肩膀上外加自己閱讀源碼調試代碼得出的結論,如果有不對的地方千萬千萬要指出。然后我再拋出一個疑問:Android ActivityThread 的 main 方法是什么時候被調用的?希望自己以后也能夠解答一下這個問題。