前期準備 1 、2、 3 步驟
使用 4、5、6步驟
1. ActivityThread main方法(ActivityThread類中)
Looper.prepareMainLooper();// 爲UI(祝線程)創建1個循環器對象
Looper.prepare() // 爲當前線程(子線程)創建1個循環器對象
Looper.loop();// 開啓輪循
2. prepareMainLooper() ( Looper類中)
- UI線程由prepareMainLooper函數內部調用自動生成Looper
- 子線程手動調用Looper.prepare()生成Looper對象
public static final void prepare() {
// sThreadLocal用於存儲線程的變量
sThreadLocal.set(new Looper(true));
}
2.1 Looper構造函數
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 1. 創建1個消息隊列對象(MessageQueue)
// 即 當創建1個Looper實例時,會自動創建一個與之配對的消息隊列對象(MessageQueue)
mThread = Thread.currentThread();
}
3. Looper.loop();( Looper類中)
開啓消息輪循
/**
* 源碼分析: Looper.loop()
* 作用:消息循環,即從消息隊列中獲取消息、分發消息到Handler
* 特別注意:
* a. 主線程的消息循環不允許退出,即無限循環
* b. 子線程的消息循環允許退出:調用消息隊列MessageQueue的quit()
*/
public static void loop() {//只貼核心代碼
//1. 獲取當前Looper的消息隊列
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
//2. next()取出消息
Message msg = queue.next(); // might block
//3. 分發消息:調用msg內部的Handler的dispatchMessage
msg.target.dispatchMessage(msg);
// 釋放資源
msg.recycleUnchecked();
}
}
3.1 next()取出消息( MessageQueue類中)
next()是MessageQueue中方法
Message next() {
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// native層調用,沒有消息會在這裏阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
}
}
3.2 dispatchMessage分發消息 ( Handler 類中)
dispatchMessage是Handler中的方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 使用者處理消息
handleMessage(msg);
}
}
使用
4 .new Handler()
4.1 Handler構造方法做了什麼事情?
- 獲取當前線程Looper
- 關聯MessageQueue
public Handler(Callback callback, boolean async) {
// 僅貼出關鍵代碼
// 1. 指定Looper對象
mLooper = Looper.myLooper();
// 2. 綁定消息隊列對象(MessageQueue)
mQueue = mLooper.mQueue;
// 獲取該Looper對象中保存的消息隊列對象(MessageQueue)
// 至此,保證了handler對象 關聯上 Looper對象中MessageQueue
}
5. 創建消息對象Message
Message msg = Message.obtain()
- Message內部維護了1個Message池,用於Message消息對象的複用
- 使用obtain()則是直接從池內獲取:避免每次都使用new重新分配內存
- 若池內無消息對象可複用,則還是用關鍵字new創建
/**
* 源碼分析:Message.obtain()
* 作用:創建消息對象
* 注:創建Message對象可用關鍵字new 或 Message.obtain()
*/
public static Message obtain() {
// Message內部維護了1個Message池,用於Message消息對象的複用
// 使用obtain()則是直接從池內獲取
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
// 建議:使用obtain()”創建“消息對象,避免每次都使用new重新分配內存
}
// 若池內無消息對象可複用,則還是用關鍵字new創建
return new Message();
}
6. 在工作線程中 發送消息到消息隊列中
最後會都會調用enqueueMessage方法(Handler類中)
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 將msg.target賦值爲this
// 即 :把 當前的Handler實例對象作爲msg的target屬性
msg.target = this;
// 請回憶起上面說的Looper的loop()中消息循環時,會從消息隊列中取出每個消息msg,然後執行msg.target.dispatchMessage(msg)去處理消息
// 實際上則是將該消息派發給對應的Handler實例
// 2. 調用消息隊列的enqueueMessage()
// 即:Handler發送的消息,最終是保存到消息隊列->>分析4
return queue.enqueueMessage(msg, uptimeMillis);
}
6.1 enqueueMessage
屬於消息隊列類(MessageQueue類)的方法
/**
* 定義:屬於消息隊列類(MessageQueue)的方法
* 作用:入隊,即 將消息 根據時間 放入到消息隊列中(Message ->> MessageQueue)
* 採用單鏈表實現:提高插入消息、刪除消息的效率
*/
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 判斷消息隊列裏有無消息
// a. 若無,則將當前插入的消息 作爲隊頭 & 若此時消息隊列處於等待狀態,則喚醒
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// b. 判斷消息隊列裏有消息,則根據 消息(Message)創建的時間 插入到隊列中
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
send…和 post…區別
- 消息對象的創建 = 內部 根據Runnable對象而封裝
- 發送到消息隊列的邏輯 =sendM(Message msg)
常問問題
- Looper 死循環爲什麼不會導致應用卡死?
會阻塞在MessageQueue的next方法nativePollOnce()方法裏,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作
Handler 同步屏障
- 如何設置同步屏障?
下面 postSyncBarrier 方法創建一個MSG對象,但是MSG的target屬性沒有賦值
MessageQueue類:
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
// .....
return token;
}
}
Handler.postXXXX/sendXXXX,最後調用enqueueMessage時,都會爲MSG的target賦值
Handler類:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 爲target賦值
msg.target = this;
//....
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看出設置同步屏障,創建的msg,內部的target是空值。Handler發送消息最終都會在enqueueMessage中進行target賦值
MessageQueue.next()
同步屏障會在取消息中體現出來。
如果設置了同步屏障,Handler會優先處理異步消息
Message next() {
//....
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 沒有消息,進入休眠狀態
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());
}
//....
}
}
應用場景:
Activity啓動後,在繪製前中有使用到同步屏障。
ViewRootImpl類:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 使用同步屏障,確保mTraversalRunnable先執行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
//....
}
}