續(xù)上篇
Android Handler之從主線程往子線程發(fā)消息(一)
簡單回顧一下Handler機(jī)制中幾個對象的主要作用
Handler機(jī)制中最重要的四個對象
Handler:負(fù)責(zé)發(fā)送消息及處理消息
Looper:復(fù)制不斷的從消息隊列中取出消息升熊,并且給發(fā)送本條消息的Handler
MessageQueue:負(fù)責(zé)存儲消息
Message:消息本身,負(fù)責(zé)攜帶數(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機(jī)制,耐心看下去并炮,最后你心中所有的疑問都會有答案
一甥郑、先來解釋第一行代碼
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為空,那么會拋出空指針異常
如果你足夠細(xì)心妨退,你會發(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插入到列表中衫哥。
也就是說娃循,當(dāng)我們調(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()方法都做了什么操作翻斟?
帶著疑問,我們進(jìn)入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ā)生了什么細(xì)節(jié)是我不知道的暑椰,那么我只好仔細(xì)看發(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)用時機(jī)避消,請往上翻翻,查看一下第四部分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機(jī)制工作的主要流程就完成了健田。
再來一個系統(tǒng)的工作示意圖
如果讀者認(rèn)真看了博客哮缺,那么到這里就應(yīng)該對Handler的基本工作流程比較清晰了。我也在前面詳細(xì)講解了流程圖中的方法調(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在一個線程中只能有一個作岖,從而導(dǎo)致MessageQueue也只能有一個
。