什么是Handler機制
Android系統(tǒng)不允許子線程訪問UI組件(子線程訪問主線程),主要是因為UI控件是非線程安全的,在多線程中并發(fā)訪問可能會導致UI控件處于不可預期的狀態(tài)。而不對UI控件的訪問加上鎖機制的原因有:
- 上鎖會讓UI控件變得復雜和低效
- 上鎖后會阻塞某些進程的執(zhí)行
而且Android系統(tǒng)為了避免ANR異常攒盈,通常需要新啟子線程來處理耗時操作,所以線程間的通信是很常見的開發(fā)場景。因此眼坏,為了解決子線程更新UI控件以及處理線程間的通信問題,系統(tǒng)提供了Handler機制酸些≡滓耄總的來說,Handler機制就是跨線程通信的消息傳遞機制魄懂。
簡介
Handler消息機制主要有四個核心類:Message(消息)沿侈、MessageQueue(消息隊列)、Looper(消息提取泵)市栗、Handler(消息處理者)缀拭。它們之間具體協(xié)作流程如下:
message(消息)
.Message的主要功能是進行消息的封裝咳短,同時可以指定消息的操作形式。我們類比生活中的郵寄信件來分析Message蛛淋,Message對象就是我們要寄出的信件咙好,它應該包括以下內(nèi)容:
收件人(消息發(fā)送給誰處理):
- Handler target屬性:
①通過Message.setTarget(Handler target)來設置,我們常用的Handler.sendMessage(Message msg )發(fā)送消息時褐荷,在調(diào)用Handler類的enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法時勾效,也是設置Message對象的target屬性。
// Handler.java 源碼
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
②在調(diào)用Messenger.send(Message message)發(fā)送消息時叛甫,Messenger對象內(nèi)部會封裝一個Handler對象葵第,這個handler對象就是Message的處理者。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Messenger messenger = new Messenger(handler);
messenger.send(message);
寄件人(對方怎么回信):
- public Messenger replyTo屬性:
發(fā)送消息時設置message.replyTo屬性合溺,在消息被接受以后卒密,接收者可以從消息對象的replyTo屬性提取出一個Messenger對象。通過這個Messenger對象就可以發(fā)送"回信"棠赛。當然你也可以發(fā)送一個不帶寄件人信息的匿名信件哮奇,所以replayTo不是必需設定的屬性。
信件內(nèi)容(要傳遞的信息或數(shù)據(jù))
- Bundle data屬性:通過Bundle來封裝需要傳遞的數(shù)據(jù)睛约;
- public Object obj屬性:
- public int arg1屬性:如果只需要存儲幾個整型數(shù)據(jù)鼎俘,arg1 和 arg2是setData()的低成本替代品;
- public int arg2屬性:如果只需要存儲幾個整型數(shù)據(jù)辩涝,arg1 和 arg2是setData()的低成本替代品贸伐。
信件ID(通過不同的ID,接收者做不同的業(yè)務處理)
- public int what屬性:在設置what屬性的時候怔揩,需要注意不同Handler之間what值沖突捉邢。
其他屬性
- Message next屬性:用來維護消息在消息隊列當中的順序(參見MessageQueue.enqueueMessage(Message msg, long when) 源碼);
- Runnable callback屬性:設置這個屬性之后商膊,在消息處理的時候?qū)r截Handler.handleMessage(Message msg)伏伐,轉(zhuǎn)而執(zhí)行callback的run()方法(參見Handler.dispatchMessage(Message msg)源碼);
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
MessageQueue(消息隊列)
消息隊列被封裝到Looper里面了晕拆,我們一般不會直接與MessageQueue打交道藐翎。我們只需要記住它是用來存放消息的單鏈表結構。隊列的順序由Message的next屬性來維護实幕。MessageQueue是整個Handler機制的核心吝镣,里面涉及很多特性我們這里都不展開講述(比如消息屏障機制)±ケ樱可以擴展閱讀深入理解MessageQueue末贾、Handler之同步屏障機制(sync barrier)。這里我們只關心隊列的入列和出列凰锡。
/**
* MessageQueue.java
* 往消息隊列添加消息的函數(shù)
* @param msg 待添加的消息
* @param when uptimeMillis(系統(tǒng)開機運行時間)+delayMillis(延遲時間未舟,由sendEmptyMessageDelayed設置)
* @return
*/
boolean enqueueMessage(Message msg, long when) {
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) {
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) {
// New head, wake up the event queue if blocked.
// 新建隊列頭圈暗,如果隊列為空掂为,或者有一個優(yōu)先級更高的消息插入到隊列頭部(譬如使用sendMessageAtTime(message,0))裕膀,
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} 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();
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;
}
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 這是一個native方法勇哗,實際作用就是通過Native層的MessageQueue阻塞nextPollTimeoutMillis毫秒的時間昼扛。
// 1.如果nextPollTimeoutMillis=-1,一直阻塞不會超時欲诺。
// 2.如果nextPollTimeoutMillis=0抄谐,不會阻塞,立即返回扰法。
// 3.如果nextPollTimeoutMillis>0蛹含,最長阻塞nextPollTimeoutMillis毫秒(超時),如果期間有程序喚醒會立即返回塞颁。
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) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
// Message設置的時間為到浦箱,將會阻塞等待。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
由上面的代碼片段我們可以知道祠锣,在使用sendMessageAtTime(Message msg, long uptimeMillis)或者sendMessageDelayed(Message msg, long delayMillis)等方法的時候酷窥,我們傳入的uptimeMillis和delayMillis并不能準確的設置消息的處理時間。執(zhí)行的策略是:
①優(yōu)先執(zhí)行隊列中靠前的Message伴网;
②如果隊列中最考前的Message還沒準備好(SystemClock.uptimeMillis() < message.when)蓬推,此時會阻塞等待。
Looper(消息提取泵)
Looper的核心作用就是通過Looper.loop()不斷地從MessageQueue中抽取Message澡腾,并將消息分發(fā)給目標處理者沸伏。我們通過介紹Looper運行的三個步驟來掌握Looper的運行原理。
準備階段(Looper.prepare())
我們知道整個消息處理機制的運作动分,就是將消息添加進消息隊列以及從消息隊列提前消息進行分發(fā)馋评。Looper.prepare()就是用來初始化創(chuàng)建Looper對象和消息列表對象的函數(shù)。因此刺啦,Looper.prepare()是我們使用Handler機制時必不可少的第一步(注意我們平時在使用主線程的handler時留特,都是直接new Handler(),具體原因我們稍后分析)玛瘸。
/**
* Looper.java
*/
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));
}
由上面的源碼可以發(fā)現(xiàn)蜕青,每個線程只能創(chuàng)建一個Looper(每個Looper又只有一個消息隊列),但是消息隊列里面可以存放多個Message糊渊,而每個Message都可以通過.setTarget(Handler target)來設置自己的Handler處理者右核。這就是Handler機制中四大要素之間的數(shù)理關系。
- prepareMainLooper():順帶說一下這個方法渺绒,它的作用就是在UI線程(主線程)調(diào)用Looper.prepare()贺喝,并且會保留一個主線程Looper對象的靜態(tài)引用菱鸥。這個引用可以通過Looper.getMainLooper()來獲取主線程Looper,方便跟子線程跟主線程通信躏鱼。
啟動和運行Looper.loop()
/**
* Looper.java
*/
public static void loop() {
// 獲取當前線程的Looper對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 獲取當前線程的消息隊列
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
// 死循環(huán)不斷的從消息隊列中提取消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
// 根據(jù)MessageQueue里面的next()方法得知氮采,要使得queue.next()返回null的情況只有兩種
// 1:MessageQueue.mPtr == 0;這里的mPtr值是調(diào)用native方法創(chuàng)建MessageQueue之后,返回的MessageQueue的引用地址染苛,通過這種方式將java層的對象與Native層的對象關聯(lián)在一起
//MessageQueue.mPtr == 0說明消息隊列創(chuàng)建失斎的;
// 2:MessageQueue.mQuitting == true;也就是調(diào)用Looper的quit()/quitSafely()方法之后茶行,next方法會返回null
return;
}
//.........省略部分代碼..........
try {
// 調(diào)用Handler的dispatchMessage(Message msg)方法處理消息
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//.........省略部分代碼..........
msg.recycleUnchecked();
}
}
根據(jù)源碼(配合上面的MessageQueue.next()方法的源碼服用)躯概,我們可以清晰的看出來Looper.loop()方法的業(yè)務邏輯就是一個死循環(huán)不停的用MessageQueue.next()方法從消息隊列中提取消息,并將消息交給target所持有的Handler進行處理(調(diào)用Handler.dispatchMessage(msg)方法)畔师。
- 因為這里是死循環(huán)娶靡,所以線程的run()方法中,一般情況下Looper.loop()調(diào)用之后的代碼邏輯不會被執(zhí)行到(特殊情況看锉,請參考下面的死循環(huán)退出條件)
private class MyThread extends Thread {
private Looper subLooper;
@Override
public void run() {
Looper.prepare();
subLooper = Looper.myLooper();
initHandler();
Looper.loop();
System.out.println("這句話永遠不會被打印姿锭,除非消息隊列初始化失敗或者調(diào)用了Looper.quit()");
}
}
- 這里的死循環(huán)其實是有退出條件的MessageQueue.next()返回null的時候會return,循環(huán)終止度陆。循環(huán)終止的情況有兩種:
①MessageQueue.mPtr == 0;這里的mPtr值是調(diào)用native方法創(chuàng)建MessageQueue之后艾凯,返回的MessageQueue的引用地址,通過這種方式將java層的對象與Native層的對象關聯(lián)在一起懂傀。MessageQueue.mPtr == 0說明消息隊列創(chuàng)建失斨菏;
②2:MessageQueue.mQuitting == true;也就是調(diào)用Looper的quit()/quitSafely()方法之后蹬蚁,next方法會返回null恃泪。 - 如果MessageQueue為空,消息列表沒有消息犀斋,將會阻塞等待贝乎。
終止和退出quit()/quitSafely()