續(xù)上篇
Android Handler之從主線程往子線程發(fā)消息(一)
簡單回顧一下Handler機制中幾個對象的主要作用
Handler機制中最重要的四個對象
Handler:負責發(fā)送消息及處理消息
Looper:復制不斷的從消息隊列中取出消息,并且給發(fā)送本條消息的Handler
MessageQueue:負責存儲消息
Message:消息本身,負責攜帶數(shù)據(jù)
那么,一個消息從發(fā)送出去,到回到Handler自己身上,這個過程具體是怎樣的?
這個就不得不去看源碼了
我們從Android Handler之從主線程往子線程發(fā)消息(一)中的
四、怎么從主線程發(fā)送消息到子線程?(雖然這種應(yīng)用場景很少)的示例代碼看起
Thread thread = new Thread(){
@Override
public void run() {
super.run();
//初始化Looper,一定要寫在Handler初始化之前
Looper.prepare();
//在子線程內(nèi)部初始化handler即可,發(fā)送消息的代碼可在主線程任意地方發(fā)送
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//所有的事情處理完成后要退出looper,即終止Looper循環(huán)
//這兩個方法都可以,有關(guān)這兩個方法的區(qū)別自行尋找答案
handler.getLooper().quit();
handler.getLooper().quitSafely();
}
};
//啟動Looper循環(huán),否則Handler無法收到消息
Looper.loop();
}
};
thread.start();
//在主線程中發(fā)送消息
handler.sendMessage();
選擇這個蹩腳的demo是為了讓大家更好的理解Handler機制,耐心看下去,最后你心中所有的疑問都會有答案
一、先來解釋第一行代碼
Looper.prepare();
這個很好解釋,只要查看Handler的構(gòu)造方法即可
//空參的構(gòu)造方法,這個方法調(diào)用了兩個參數(shù)的構(gòu)造方法
public Handler() {
this(null, false);
}
//兩個參數(shù)的構(gòu)造方法
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
看到了沒有,Handler的構(gòu)造方法中會驗證Looper,如果Looper為空,那么會拋出空指針異常
如果你足夠細心,你會發(fā)現(xiàn),Handler在構(gòu)造方法中還做了一件事,
將自己的一個全局消息隊列對象(mQueue)指向了Looper中的消息隊列
即構(gòu)造方法中的這行代碼
mQueue = mLooper.mQueue;
先記住,有用
二、第二行代碼是初始化了Hanlder并且重寫HandleMessage()方法
這個沒什么好解釋的
三、我們調(diào)用了
Looper.loop()方法,這個方法的具體原理隨后解釋
四、handler.sendMessage(message)的主要作用
最接近我們使用的就是handler.sendMessage(message);這行代碼了,那么我們從這行代碼看起,看看這行代碼之后發(fā)生了什么,為了方便大家看到,我把代碼執(zhí)行流程畫了出來

紅線畫出來的代表代碼是哪個類的,可以看到,我們sendMessage()之后代碼通過圖中所示的幾個方法,最終執(zhí)行到了MessageQueue的enqueueMessage()方法。也就是說,接下來我們要看的就是MessageQueue中的方法了。
上源碼
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
為了方便閱讀,我刪減了部分代碼
可以看到,MessageQueue是一個單向列表結(jié)構(gòu),而MessageQueue的enqueueMessage()方法主要做的事情就是將Handler發(fā)送過來的Message插入到列表中。
也就是說,當我們調(diào)用handler.senMessage()方法的時候,最終的結(jié)果只是將這個消息插入到了消息隊列中
上面的流程圖中有一個問題:
最后一行代碼queue.enqueueMessage()中的queue對象是什么時候初始化的?
還記得本篇博客在解釋Looper.prepare()方法部分的最后一段話嗎?
不記得了就往上翻一翻
發(fā)送消息的工作已經(jīng)完成,那么Looper是什么時候取的消息,取出來消息又是怎么送回給Handler的呢?現(xiàn)在我們就不得不看Looper.loop()方法了
Looper.loop()方法
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}
}
}
同樣的,為了降低閱讀難度,我刪掉了大部分的代碼,只留下了這么幾行核心代碼,現(xiàn)在我們來看這幾行代碼的邏輯
- 這是一個死循環(huán)
- 這個循環(huán)的目的是從MessageQueue中取出消息
- 取消息的方法是MessageQueue.next()方法
- 取出消息后調(diào)用message.target對象的dispatchMessage()方法分發(fā)消息
- 循環(huán)跳出的條件是MessageQueue.next()方法返回了null
不過,看到這里我們應(yīng)該自然會想到,message.target.是哪個對象?
該對象的dispatchMessage()方法都做了什么操作?
帶著疑問,我們進入Message類中去尋找target
public final class Message implements Parcelable {
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
}
看到了吧,Message類中有一個成員變量 target,而這個target是一個Handler,也就是說,在Looper從MessageQueue中取出Message之后,調(diào)用了Handler的dispatchMessage()方法。
這里我們不禁要問,這個target指向了哪個Handler
帶著這個疑問,我翻遍了Message類,也沒有看到我想要的答案。此時我就想,既然Handler發(fā)送了消息就能接收到消息,那么會不會是在發(fā)送消息的途中發(fā)生了什么細節(jié)是我不知道的,那么我只好仔細看發(fā)送消息過程中的代碼,終于讓我發(fā)現(xiàn)了這個
//Handler的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在這個方法的第一行代碼就是,將message的target屬性賦值為發(fā)送message的handler自身。如果讀者忘記了這個方法的調(diào)用時機,請往上翻翻,查看一下第四部分sendMeeage()中的流程圖
也就是說,Looper取出消息后,調(diào)用了發(fā)送消息的Handler的dispatchMessage()方法,并且將message本身作為參數(shù)傳了回去。到此時,代碼的執(zhí)行邏輯又回到了Handler中。
接著看handler的dispatchMessage()方法
/**
*handler的方法
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
看到這里面一個我們非常熟悉到方法了沒有?---handleMessage()方法,對,這個方法就是我們經(jīng)常要重寫的handleMessage()方法,也是我們處理消息時候的邏輯。
到這里,Handler機制工作的主要流程就完成了。
再來一個系統(tǒng)的工作示意圖

如果讀者認真看了博客,那么到這里就應(yīng)該對Handler的基本工作流程比較清晰了。我也在前面詳細講解了流程圖中的方法調(diào)用過程。不過還有一些問題我們沒有處理
一個線程中最多可以有幾個Looper和幾個MessageQueue?
我們來看Looper的構(gòu)造方法即初始化方法
//Looper暴露出的靜態(tài)初始化方法
//這個方法會調(diào)用下面的私有靜態(tài)方法
public static void prepare() {
prepare(true);
}
//Looper私有的靜態(tài)方法
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));
}
//私有的構(gòu)造方法,禁止外界調(diào)用
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//從sThreadLocal中獲取一個Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
解釋一下上面三個方法
- 我們只能通過Looper.prepare()方法去初始化一個Looper
- Looper.prepare(boolean)方法的邏輯是一個線程中只能有一個Looper對象,否則在第二次嘗試初始化Looper的時候,就會拋出異常。
- 以線程為單位存儲Looper的主要邏輯是通過ThreadLocal實現(xiàn)的
- 私有的構(gòu)造方法,禁止外界任意new出一個Looper
通過這段邏輯我們可以看出,
一個線程中最多有一個Looper
接著看Looper的構(gòu)造方法,里面有一行代碼
mQueue = new MessageQueue(quitAllowed);
是的,我們的MessageQueue是隨著Looper的初始化而初始化的。那么,MessageQueue能不能隨意的被new出來呢?
//MessageQueue的構(gòu)造方法
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
可以看到,MessageQuede的構(gòu)造方法是default的,也就是說,只有跟MessageQueue同一個包下才可以實例化MessageQueue,換句話說,我們用戶是無法直接new一個MessageQueue對象出來的。而因為Looper在一個線程中只能有一個,從而導致MessageQueue也只能有一個。