Handler---4部曲---1. 總體流程
Handler---4部曲---2. ThreadLocal 存儲主流程
Handler---4部曲---3. MessageQueue隊列
Handler---4部曲---4.細節補充
本章內容
一. 數據結構
MessageQueue 3個成員變量
ArrayList<IdleHandler> mIdleHandlers --->
boolean mBlocked; --->next()是否阻塞 標記
Message mMessages; --->隊列頭
Message 3個成員變量
Handler target; //當前handler
long when; //消息延遲時間
Message next;//下一個消息
二. 消息分類
2.1 同步消息
2.2 異步消息
2.3 同步屏障消息 --->撤銷屏障removeSyncBarrier()
2.4 空閒消息
三. 4種消息的添加方式
3.1 同步消息
enqueueMessage(Message msg, long when) 添加
boolean enqueueMessage(Message msg, long when) {
//用此方法添加到隊列, msg一定要給target賦值
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
msg.when = when;
Message p = mMessages;
//如果 首次進來 || 時間=0 || 當前時間<隊列頭的時間
//就把當前消息加到隊列頭
if (p == null || when == 0 || when < p.when) { //
msg.next = p;
mMessages = msg;
} else {
//把當前msg按照when 從小到大插入隊列中
Message prev;
for (;;) {
prev = p; //
p = p.next;
if (p == null || when < p.when) {
break;
}
}
//此時 prev.when<msg.when<p.when
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
return true;
}
總結:把msg消息按時間順序, 從小到大添加到隊列中.
3.2 異步消息
和同步消息差不多, 只是把msg標記爲異步.
msg.setAsynchronous(true)
MessageQueue.enqueueMessage(msg, 時間xxx)
3.3 同步屏障消息
//添加同步屏障消息
int token = MessageQueue.postSyncBarrier();
//移除同步屏障消息
removeSyncBarrier(token);
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
//獲取Message 對象
final Message msg = Message.obtain();
msg.when = when; //時間賦值---當前時間
// 以下的操作,和普通消息一樣,按時間順序 小到大排列
// 最後msg就是隊列頭.
Message prev = null;
Message p = mMessages;
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;
}
}
總結:
msg內部創建,也是按時間順序進行排列的,
msg.target = null
msg.when = 當前時間
3.4 空閒消息
將消息添加到mIdleHandlers集合中
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
四. next()消息處理
Message next() {
//mIdleHandlers集合數量
int pendingIdleHandlerCount = -1;
//阻塞休眠時間
int nextPollTimeoutMillis = 0;
for (;;) {
//阻塞隊列 nextPollTimeoutMillis休眠時間
// -1 一直休眠, 等待喚醒
// 0 不休眠, 馬上執行
// 大於0,休眠時間
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//判斷是否有異步消息, 如果有,循環隊列, 取出異步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//注意:如果異步消息 的時間 > 當前時間, 則會一直等待, 其它消息得不到執行, 直到異步消息執行完爲止.
//此時msg代表取出的消息
if (msg != null) { //有消息
if (now < msg.when) { //沒有到執行時間
//休眠時間 = (時間差,Integer.MAX_VALUE)中的小值.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 馬上執行消息
mBlocked = false; //標記線程不阻塞
if (prevMsg != null) { //異步消息時
prevMsg.next = msg.next;
} else { //同步消息時,隊列頭=下一個msg
mMessages = msg.next;
}
msg.next = null;
return msg;
}
} else {
// 如果沒有消息,休眠時間--->一直休眠
nextPollTimeoutMillis = -1;
}
//是否退出(下一篇會講)
if (mQuitting) {
dispose();
return null;
}
//>>>>>>>>>>>>>>>下面是處理 空閒消息 mIdleHandlers>>>>>>>>>>>>>>>>>>>>>>>>>>
if (pendingIdleHandlerCount < 0 //首次進入
//&& (沒有消息 || 當前時間 < 隊列頭時間)
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//沒有空閒消息處理
if (pendingIdleHandlerCount <= 0) {
mBlocked = true; //標記,線程阻塞
continue;
}
//空閒消息List 轉 數組
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//循環處理空閒消息
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 釋放數組內存
boolean keep = false;
try {
// //返回false纔會刪除任務, 否則重複執行
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置變量
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
Message消息處理:
- 首先獲取隊列頭:Message msg = mMessages;
- 判斷是否有同步屏障的消息. 如果有,會循環隊列, 去找異步消息msg
①. 如果有異步消息,msg = 異步消息
②. 如果沒有異步消息,msg==null. - 如果msg==null, 線程進入休眠,nextPollTimeoutMillis = -1;
- 如果 msg!=null;
①. 當前時間 < msg.when, nextPollTimeoutMillis(休眠時間) = msg.when-當前時間
②. 當前時間 >= msg.when,取出msg給loop去處理
------>同步消息時,隊列頭=下一個msg, mMessages = msg.next
空閒消息處理:
沒有msg消息處理的時候,纔會處理空閒消息
- mIdleHandlers集合 轉 數組
- 遍歷數組 處理任務
- 任務返回false時, 纔會從mIdleHandlers集合移除任務.
- 重置變量
endingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;