Handler源碼學(xué)習(xí)(一)流程
Handler源碼學(xué)習(xí)(二)Message對(duì)象池
Handler源碼學(xué)習(xí)(三)MessageQueue入隊(duì)插隊(duì)
1.消息入隊(duì)
消息隊(duì)列與Message對(duì)象池的結(jié)構(gòu)很像焦影,也是通過(guò)對(duì)象之間通過(guò)next指向形成鏈表結(jié)構(gòu)
這時(shí)候加入一個(gè)msg消息,先來(lái)看如果消息隊(duì)列為空的情況
//判斷消息隊(duì)列為空時(shí),會(huì)直接將這個(gè)msg賦值給mMessage透乾,并將p賦值給msg.next,這時(shí)next當(dāng)然時(shí)null
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;
}
假設(shè)現(xiàn)在隊(duì)列中已經(jīng)有了兩個(gè)消息,這兩個(gè)消息的排練是按照?qǐng)?zhí)行時(shí)間蓝撇,如果時(shí)間相同則是按照入隊(duì)先后排列。來(lái)一步步分析陈莽,可以先看后面的圖渤昌,把思路理清虽抄,再看源碼
else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
//首先聲明了一個(gè)message對(duì)象prev,從字面意思理解是前一個(gè)message的意思
Message prev;
for (;;) {
//接著進(jìn)入一個(gè)死循環(huán)独柑,將p賦值給prev迈窟,看看前面的代碼可以知道,p指向的是mMessages,所以這里
//是將prev指向了mMessage忌栅,再下一次循環(huán)的時(shí)候车酣,prev則是指向了第二個(gè)message,依次類推
prev = p;
//接著將p指向p.next也就是mMessages.next,也就是消息鏈表中的第二個(gè)message
p = p.next;
//接下來(lái)判斷兩件事
//1.p==null索绪,說(shuō)明沒(méi)有下一個(gè)消息了湖员,跳出循環(huán)
//2.p!=null,并且當(dāng)前需要入隊(duì)的這個(gè)message的執(zhí)行時(shí)間是小于隊(duì)列中這個(gè)任務(wù)的執(zhí)行時(shí)間的
//也就是說(shuō)這個(gè)入隊(duì)的message需要比隊(duì)列中的這個(gè)message先執(zhí)行瑞驱,也跳出循環(huán)
//3.如果這兩個(gè)條件都不滿足的話娘摔,則繼續(xù)跟隊(duì)列中的下一個(gè)消息進(jìn)行對(duì)比,直到滿足條件唤反,或者到
//隊(duì)列的末尾
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//跳出循環(huán)后做了兩件事情
// 1.將入隊(duì)的這個(gè)消息的next指向循環(huán)中獲取到的應(yīng)該排在這個(gè)消息之后的message
msg.next = p; // invariant: p == prev.next
//將msg前面那個(gè)message.next指向了msg
prev.next = msg;
//到這里就將一個(gè)message完成了入隊(duì)
//入隊(duì)的過(guò)程是線程安全的
}
2.取出消息
這里貼的不是完整代碼凳寺,而是取出message的核心邏輯代碼,這里其實(shí)分了兩個(gè)部分拴袭,第一個(gè)部分是消息插隊(duì)读第,這個(gè)在第三節(jié)敘述,第二個(gè)部分是正常的消息拥刻,其實(shí)取出消息的部分比較簡(jiǎn)單,注釋也比較清晰
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//隊(duì)列的第一個(gè)message
Message msg = mMessages;
//正常取出消息
if (msg != null) {
//1.首先判斷當(dāng)前時(shí)間是否小于了msg的執(zhí)行時(shí)間父泳,
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//(翻譯)需要執(zhí)行的消息還沒(méi)有到執(zhí)行時(shí)間般哼,設(shè)置一個(gè)喚醒時(shí)間,當(dāng)?shù)搅藞?zhí)行時(shí)間時(shí)喚醒
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.正常取出消息惠窄,這里邏輯比較簡(jiǎn)單蒸眠,不再贅述
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
3.消息插隊(duì)
首先需要知道的是整個(gè)android系統(tǒng)其實(shí)主要是依賴消息機(jī)制來(lái)處理事件,比如說(shuō)點(diǎn)擊事件等等杆融,都是通過(guò)handler發(fā)送消息進(jìn)行處理楞卡,但是這些系統(tǒng)消息相較于程序自己發(fā)送的消息,應(yīng)該要優(yōu)先執(zhí)行脾歇,所以就涉及到了消息插隊(duì)
- isAsynchronous() — 如何將系統(tǒng)消息和程序發(fā)送的消息區(qū)分開來(lái)
在閱讀Hanlder的源碼的時(shí)候蒋腮,可以看到一個(gè)@hide的構(gòu)造方法,傳入一個(gè)布爾值藕各,從字面意思理解是異步
public Handler(boolean async) {
this(null, async);
}
mAsynchronous = async;//賦值給mAsychronous
//消息入隊(duì)的時(shí)候池摧,msg.setAsynchronous(true);將這個(gè)msg標(biāo)記為異步
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
為什么要標(biāo)記為異步呢?是要新開一個(gè)線程來(lái)執(zhí)行么激况?可是最后也是發(fā)送到looper所綁定的消息隊(duì)列中啊作彤,貌似這里并沒(méi)有異步膘魄,接著看上一節(jié)提到的消息插隊(duì)的源碼,在這里看到調(diào)用了一個(gè)方法msg.isAsynchronous()竭讳,貌似這個(gè)布爾值并不是跟異步有關(guān)系创葡,而是將這個(gè)消息做了一個(gè)標(biāo)記而已
前面的消息入隊(duì)分析可以知道,雖然被標(biāo)記了系統(tǒng)消息绢慢,但是還是按照消息隊(duì)列的規(guī)則去入隊(duì)蹈丸,那么如何做到優(yōu)先取出這個(gè)任務(wù)呢,一步步看
Message prevMsg = null;
Message msg = mMessages;
//1.判斷當(dāng)前第一個(gè)消息不為空
//2.重點(diǎn)來(lái)了,msg.target == null,如果這個(gè)消息的target為空呐芥,看過(guò)handler源碼可以知道逻杖,
//只要是通過(guò)handler發(fā)送消息,就會(huì)將這個(gè)msg的target指向發(fā)送消息的handler思瘟,否則也無(wú)法處理這個(gè)消息
//那么為什么這里會(huì)出現(xiàn)target為空呢荸百?先看if代碼塊里面的代碼
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//進(jìn)入一個(gè)循環(huán),注意while中的判斷條件
do {
//不斷地取下一個(gè)消息來(lái)匹配判斷條件
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
//只有當(dāng)msg不為空 并且當(dāng)前的這個(gè)消息是異步的滨攻,也就是說(shuō)是系統(tǒng)消息够话,則跳出循環(huán)。
//跳出循環(huán)后就走到了正常取消息的代碼中光绕,取出的正是這個(gè)系統(tǒng)消息女嘲,發(fā)現(xiàn)插隊(duì)就成功了
}
上面的分析已經(jīng)插隊(duì)成功,但是還有疑點(diǎn)诞帐,到底是怎么進(jìn)入到if代碼塊中的
// Stalled by a barrier. Find the next asynchronous message in the queue.
作者在這里給了一條注釋欣尼,通過(guò)一個(gè)阻塞塊來(lái)停止正常的隊(duì)列,找到隊(duì)列中的第一個(gè)系統(tǒng)消息
在MessageQueue中試圖搜索barrier停蕉,發(fā)現(xiàn)一個(gè)方法愕鼓,
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
//判斷將這個(gè)消息插入到隊(duì)列中的哪個(gè)位置
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
加入隊(duì)列的代碼已經(jīng)很熟悉了,可以看到在這段代碼中其實(shí)入隊(duì)了一個(gè)沒(méi)有target的消息慧起,而最上面那個(gè)public方法中可以看到傳入的when是當(dāng)前的系統(tǒng)時(shí)間菇晃,也就是說(shuō)如果調(diào)用這個(gè)方法會(huì)在消息隊(duì)列的頭部插入一個(gè)沒(méi)有target的message,到這里思路就比較清晰了蚓挤,但是這個(gè)消息肯定不能一直在隊(duì)列中磺送,否則整個(gè)隊(duì)列的正常消息就永遠(yuǎn)無(wú)法處理,所以相對(duì)應(yīng)還有一個(gè)remove灿意。注釋中說(shuō)明估灿,這個(gè)方法應(yīng)該跟插入的方法匹配使用。
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
當(dāng)發(fā)送系統(tǒng)消息時(shí) 會(huì)在消息隊(duì)列中插入一個(gè)target為空的message脾歧,在取出消息時(shí)如果發(fā)現(xiàn)了這個(gè)消息甲捏,就跳過(guò)所有的正常消息,返回最近的一個(gè)系統(tǒng)消息鞭执,然后將這個(gè)標(biāo)記消息從隊(duì)列中remove司顿。
整個(gè)Handler源碼學(xué)習(xí)系列筆記就完結(jié)了芒粹,當(dāng)然現(xiàn)在也沒(méi)有到特別深入的程度,但了解了整套Handler的消息機(jī)制后大溜,相信對(duì)于源碼的閱讀能力和編程思路上也有挺大的提高化漆。