開篇
上一篇說到曹操敗走華容道后...咳咳兆沙,不好意思走錯(cuò)片場了候引。需要看上篇的同學(xué)出門左拐點(diǎn)主頁觀看
8.quit()
public void quit() {
mQueue.quit(false); // 消息移除
}
public void quitSafely() {
mQueue.quit(true); // 安全消息移除
}
Looper.quit() 方法的實(shí)現(xiàn)最終調(diào)用的是 MessageQueue.quit() 方法码倦。
9.MessageQueue.quit()
void quit(boolean safe) {
if (!mQuitAllowed) { 當(dāng) mQuitAllowed 為 false址儒,表示不運(yùn)行退出捕发,強(qiáng)行調(diào)用 quit() 會(huì)拋出異常
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
消息退出的方式:
######1.當(dāng) safe = true 時(shí)庙楚,只移除尚未觸發(fā)的所有消息面哥,對于正在觸發(fā)的消息并不移除
######2.當(dāng) safe = flase 時(shí)哎壳,移除所有的消息
#三Handler
-------------------------------------------------------------
##構(gòu)造函數(shù)
-------------------------------------------------------------
### 無參構(gòu)造
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
// 匿名類、內(nèi)部類或本地類都必須申明為static幢竹,否則會(huì)警告可能出現(xiàn)內(nèi)存泄露
if (FIND_POTENTIAL_LEAKS) { // 默認(rèn)為 false
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
對于 Handler 的無參構(gòu)造方法耳峦,默認(rèn)采用當(dāng)前線程 TLS 中的 Looper 對象,并且 callback 回調(diào)方法為 null焕毫,且消息為同步處理方式蹲坷。只要執(zhí)行的 Looper.prepare() 方法驶乾,那么便可以獲取有效的 Looper 對象。
1.dispatchMessage()
在 Looper.loop() 中循签,當(dāng)發(fā)現(xiàn)有消息時(shí)级乐,調(diào)用消息的目標(biāo) handler,執(zhí)行 dispatchMessage() 方法來分發(fā)消息县匠。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 當(dāng) Message 存在回調(diào)方法风科,回調(diào) msg.callback.run() 方法
handleCallback(msg);
} else {
if (mCallback != null) {
// 當(dāng) Handler 存在 Callback 成員變量時(shí),回調(diào)方法 handleMessage()
if (mCallback.handleMessage(msg)) {
return;
}
}
// Handler 自身的回調(diào)方法 handleMessage()
handleMessage(msg);
}
}
我們需要重點(diǎn)分析下這個(gè)函數(shù):
首先會(huì)判斷 msg.callback 存不存在乞旦,msg.callback 是 Runnable 類型贼穆,如果 msg.callback 存在,那么說明該 Message 是通過執(zhí)行 Handler 的 post() 系列方法將 Message 放入到消息隊(duì)列中的兰粉,這種情況下會(huì)執(zhí)行 handleCallback(msg)故痊。
2.handleCallback
源碼如下:
private static void handleCallback(Message message) {
message.callback.run();
}
這樣我們就清楚地看到我們執(zhí)行了 msg.callback 的 run 方法,也就是執(zhí)行了 post() 所傳遞的 Runnable 對象的 run 方法玖姑。
3.mCallback
如果我們不是通過 post() 系列方法將 Message 放入到消息隊(duì)列中的愕秫,那么 msg.callback 就是 null ,代碼繼續(xù)往下執(zhí)行焰络。
接著我們會(huì)判斷 Handler 的成員字段 mCallback 存不存在戴甩。mCallback 是 Hanlder.Callback 類型的,我們在上面提到過闪彼,在 Handler 的構(gòu)造函數(shù)中我們可以傳遞 Hanlder.Callback 類型的對象甜孤,該對象需要實(shí)現(xiàn) handleMessage 方法,如果我們在構(gòu)造函數(shù)中傳遞了該 Callback 對象备蚓,那么我們就會(huì)讓 Callback 的 handleMessage 方法來處理 Message课蔬。
final Callback mCallback;
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
如果我們在構(gòu)造函數(shù)中沒有傳入 Callback 類型的對象,那么 mCallback 就為 null 郊尝,那么我們會(huì)調(diào)用 Handler 自身的 hanldeMessage 方法二跋,該方法默認(rèn)是個(gè)空方法,我們需要自己重寫實(shí)現(xiàn)該方法流昏。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) { // 空函數(shù)
}
綜上所述扎即,我們可以看到 Handler 提供了三種途徑處理 Message ,而且處理有前后優(yōu)先級(jí)之分:首先嘗試讓 post() 中傳遞的 Runnable 執(zhí)行况凉,其次嘗試讓 Handler 構(gòu)造函數(shù)中傳入的 Callback 的 handleMessage 方法處理谚鄙,最后才是讓 Handler 自身的 handleMessage 方法處理Message。
4.Callback
Callback 是 Handle r中的內(nèi)部接口刁绒,需要實(shí)現(xiàn)其內(nèi)部的 handleMessage 方法闷营,Callback 代碼如下:
public interface Callback {
public boolean handleMessage(Message msg);
}
Handler.Callback 是用來處理 Message 的一種手段,如果沒有傳遞該參數(shù),那么就應(yīng)該重寫 Handler 的 handleMessage 方法傻盟,也就是說為了使得 Handler 能夠處理 Message 速蕊,有兩種辦法:
1.向 Hanlder 的構(gòu)造函數(shù)傳入一個(gè) Handler.Callback 對象,并實(shí)現(xiàn) Handler.Callback 的 handleMessage 方法
2.無需向 Hanlder 的構(gòu)造函數(shù)傳入 Handler.Callback 對象规哲,但是需要重寫 Handler 本身的 handleMessage 方法
也就是說無論哪種方式,我們都得通過某種方式實(shí)現(xiàn) handleMessage 方法诽表,這點(diǎn)與 Java 中對 Thread 的設(shè)計(jì)有異曲同工之處唉锌。
在Java中,如果我們想使用多線程竿奏,有兩種辦法:
1. 向 Thread 的構(gòu)造函數(shù)傳入一個(gè) Runnable 對象袄简,并實(shí)現(xiàn) Runnable 的 run 方法
2. 無需向 Thread 的構(gòu)造函數(shù)傳入 Runnable 對象,但是要重寫 Thread 本身的 run 方法
所以只要用過多線程 Thread议双,應(yīng)該就對 Hanlder 這種需要實(shí)現(xiàn) handleMessage 的兩種方式了然于心了痘番。
在之前分析 Handler(用法篇)的時(shí)候我們講到過兩種重要的方法:sendMessage() 和 post(),我們從源碼角度進(jìn)行進(jìn)一步分析平痰!
四 sendMessage
我們看下 sendMessage() 源碼處理流程:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
1.sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); // 最終調(diào) sendMessageAtTime()
}
通過以上代碼可以看書:sendMessage() 調(diào)用了 sendMessageDelayed() ,sendMessageDelayed() 又調(diào)用了 sendMessageAtTime()伍纫。
Handler 中還有 sendEmptyMessage() 方法:
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); // 最終還是要調(diào) sendMessageAtTime()
}
由此可見所有的 sendMessage 方法和 sendEmptyMessage 最終都調(diào)用了 sendMessageAtTime 方法宗雇。
2.post
我們看下 post() 源碼處理流程:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
可以看到內(nèi)部調(diào)用了 getPostMessage 方法,該方法傳入一個(gè) Runnable 對象莹规,得到一個(gè) Message 對象赔蒲。
3.getPostMessage
getPostMessage() 的源碼如下:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
通過上面的代碼我們可以看到在 getPostMessage 方法中,我們創(chuàng)建了一個(gè) Message 對象良漱,并將傳入的 Runnable 對象賦值給 Message 的 callback 成員字段舞虱,然后返回該 Message ,然后在 post 方法中該攜帶有 Runnable 信息的 Message 傳入到 sendMessageDelayed 方法中母市。由此我們可以看到所有的 post 方法內(nèi)部都需要借助 sendMessage 方法來實(shí)現(xiàn)矾兜,所以 post() 與 sendMessage() 并不是對立關(guān)系,而是 post() 依賴 sendMessage() 患久,所以 post() 方法可以通過 sendMessage() 方法向消息隊(duì)列中傳入消息椅寺,只不過通過 post() 方法向消息隊(duì)列中傳入的消息都攜帶有 Runnable 對象(Message.callback)。
五 sendMessageAtTime
通過分別分析 sendEmptyMessage()蒋失、post() 方法與 sendMessage() 方法之間的關(guān)系返帕,我們可以看到在 Handler 中所有可以直接或間接向消息隊(duì)列發(fā)送 Message 的方法最終都調(diào)用了 sendMessageAtTime 方法,該方法的源碼如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
1.enqueueMessage
我們發(fā)現(xiàn) sendMessageAtTime() 方法內(nèi)部調(diào)用了 enqueueMessage() 函數(shù):
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
我們需要重點(diǎn)注意兩行代碼:
msg.target = this; // 將 Message 的 target 綁定為當(dāng)前的 Handler
// 變量 queue 表示的是 Handler 所綁定的消息隊(duì)列 MessageQueue 篙挽,通過調(diào)用 queue.enqueueMessage(msg, uptimeMillis) 將 Message 放入到消息隊(duì)列中荆萤。
queue.enqueueMessage(msg, uptimeMillis);
還記得我們之前在分析 Looper 的時(shí)候,最終提到的 dispatchMessage() 嗎铣卡?我們回憶一下:
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
六 MessageQueue
每個(gè)線程內(nèi)部都維護(hù)了一個(gè)消息隊(duì)列 —— MessageQueue链韭。消息隊(duì)列 MessageQueue邑闲,顧名思義,就是存放消息的隊(duì)列梧油。那隊(duì)列中存儲(chǔ)的消息是什么呢苫耸?
假設(shè)我們在UI界面上單擊了某個(gè)按鈕,而此時(shí)程序又恰好收到了某個(gè)廣播事件儡陨,那我們?nèi)绾翁幚磉@兩件事呢褪子?因?yàn)橐粋€(gè)線程在某一時(shí)刻只能處理一件事情,不能同時(shí)處理多件事情骗村,所以我們不能同時(shí)處理按鈕的單擊事件和廣播事件嫌褪,我們只能挨個(gè)對其進(jìn)行處理,只要挨個(gè)處理就要有處理的先后順序胚股。
為此Android把UI界面上單擊按鈕的事件封裝成了一個(gè) Message 笼痛,將其放入到 MessageQueue 里面去,即將單擊按鈕事件的 Message 入棧到消息隊(duì)列中琅拌,然后再將廣播事件的封裝成以 Message 缨伊,也將其入棧到消息隊(duì)列中。
也就是說一個(gè) Message 對象表示的是線程需要處理的一件事情进宝,消息隊(duì)列就是一堆需要處理的 Message 的池刻坊。線程 Thread 會(huì)依次取出消息隊(duì)列中的消息,依次對其進(jìn)行處理党晋。
MessageQueue 中有兩個(gè)比較重要的方法谭胚,一個(gè)是 enqueueMessage 方法,一個(gè)是 next 方法未玻。enqueueMessage 方法用于將一個(gè) Messag e放入到消息隊(duì)列 MessageQueue 中灾而,next 方法是從消息隊(duì)列 MessageQueue 中阻塞式地取出一個(gè) Message。在 Android 中扳剿,消息隊(duì)列負(fù)責(zé)管理著頂級(jí)程序?qū)ο螅ˋctivity旁趟、BroadcastReceiver等)以及由其創(chuàng)建的所有窗口。
七 創(chuàng)建MessageQueue
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
// 通過 native 方法初始化消息隊(duì)列舞终,其中 mPtr 是供 native 代碼使用
mPtr = nativeInit();
}
next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) { // 當(dāng)消息循環(huán)已經(jīng)退出轻庆,則直接返回
return null;
}
// 循環(huán)迭代的首次為 -1
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞操作,當(dāng)?shù)却?nextPollTimeoutMillis 時(shí)長敛劝,或者消息隊(duì)列被喚醒余爆,都會(huì)返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 當(dāng)消息 Handler 為空時(shí),查詢 MessageQueue 中的下一條異步消息 msg夸盟,則退出循環(huán)
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 當(dāng)異步消息觸發(fā)時(shí)間大于當(dāng)前時(shí)間蛾方,則設(shè)置下一次輪詢的超時(shí)時(shí)長
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲取一條消息,并返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
// 設(shè)置消息的使用狀態(tài),即 flags |= FLAG_IN_USE
msg.markInUse();
// 成功地獲取 MessageQueue 中的下一條即將要執(zhí)行的消息
return msg;
}
} else {
// 沒有消息
nextPollTimeoutMillis = -1;
}
// 消息正在退出桩砰,返回null
if (mQuitting) {
dispose();
return null;
}
// 當(dāng)消息隊(duì)列為空拓春,或者是消息隊(duì)列的第一個(gè)消息時(shí)
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 沒有 idle handlers 需要運(yùn)行,則循環(huán)并等待
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 只有第一次循環(huán)時(shí)亚隅,會(huì)運(yùn)行 idle handlers硼莽,執(zhí)行完成后,重置 pendingIdleHandlerCount 為 0
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 去掉 handler 的引用
boolean keep = false;
try {
keep = idler.queueIdle(); // idle 時(shí)執(zhí)行的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置 idle handler 個(gè)數(shù)為 0煮纵,以保證不會(huì)再次重復(fù)運(yùn)行
pendingIdleHandlerCount = 0;
// 當(dāng)調(diào)用一個(gè)空閑 handler 時(shí)懂鸵,一個(gè)新 message 能夠被分發(fā),因此無需等待可以直接查詢 pending message
nextPollTimeoutMillis = 0;
}
}
nativePollOnce 是阻塞操作行疏,其中 nextPollTimeoutMillis 代表下一個(gè)消息到來前匆光,還需要等待的時(shí)長;當(dāng) nextPollTimeoutMillis = -1 時(shí)酿联,表示消息隊(duì)列中無消息终息,會(huì)一直等待下去。
當(dāng)處于空閑時(shí)贞让,往往會(huì)執(zhí)行 IdleHandler 中的方法周崭。當(dāng) nativePollOnce() 返回后,next() 從 mMessages 中提取一個(gè)消息震桶。
八 enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
// 每一個(gè)普通 Message 必須有一個(gè) target
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
// 正在退出時(shí)休傍,回收 msg,加入到消息池
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// p 為 null (代表MessageQueue沒有消息) 或者 msg 的觸發(fā)時(shí)間是隊(duì)列中最早的蹲姐,則進(jìn)入該該分支
msg.next = p;
mMessages = msg;
needWake = mBlocked; // 當(dāng)阻塞時(shí)需要喚醒
} else {
// 將消息按時(shí)間順序插入到 MessageQueue。一般地人柿,不需要喚醒事件隊(duì)列柴墩,除非
// 消息隊(duì)頭存在 barrier,并且同時(shí) Message 是隊(duì)列中最早的異步消息
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;
}
// 消息沒有退出凫岖,我們認(rèn)為此時(shí) mPtr != 0
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue 是按照 Message 觸發(fā)時(shí)間的先后順序排列的江咳,隊(duì)頭的消息是將要最早觸發(fā)的消息。當(dāng)有消息需要加入消息隊(duì)列時(shí)哥放,會(huì)從隊(duì)列頭開始遍歷歼指,直到找到消息應(yīng)該插入的合適位置,以保證所有消息的時(shí)間順序甥雕。
九 removeMessages()
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// 從消息隊(duì)列的頭部開始踩身,移除所有符合條件的消息
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// 移除剩余的符合要求的消息
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
這個(gè)移除消息的方法,采用了兩個(gè) while 循環(huán)社露,第一個(gè)循環(huán)是從隊(duì)頭開始挟阻,移除符合條件的消息,第二個(gè)循環(huán)是從頭部移除完連續(xù)的滿足條件的消息之后,再從隊(duì)列后面繼續(xù)查詢是否有滿足條件的消息需要被移除附鸽。
總結(jié)
最后用一張圖脱拼,來表示整個(gè)消息機(jī)制:
圖解:
? Handler通過sendMessage()發(fā)送Message到MessageQueue隊(duì)列;
? Looper通過loop()坷备,不斷提取出達(dá)到觸發(fā)條件的Message熄浓,并將Message交給target來處理;
? 經(jīng)過dispatchMessage()后省撑,交回給Handler的handleMessage()來進(jìn)行相應(yīng)地處理赌蔑。
? 將Message加入MessageQueue時(shí),處往管道寫入字符丁侄,可以會(huì)喚醒loop線程惯雳;如果MessageQueue中沒有Message,并處于Idle狀態(tài)鸿摇,則會(huì)執(zhí)行IdelHandler接口中的方法石景,往往用于做一些清理性地工作。