1.概述
Handler一般在Android的UI線程和子線程間通信使用甩十,之前我們在分析AsyncTask源碼的時(shí)候就發(fā)現(xiàn)其實(shí)她的內(nèi)部是Handler和Thread的一個(gè)組合转晰,子線程做耗時(shí)的計(jì)算任務(wù)什燕,Handler負(fù)責(zé)傳遞和接收結(jié)果回挽。AsyncTask的源碼解析請移步到我的另一篇文章AsyncTask源碼分析。
2.用法
我們這里簡單回顧一下Handler的用法:
創(chuàng)建一個(gè)Handler用來接收消息
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
Logger.d(TAG,bundle.getString("Content"));
}
};
發(fā)送消息
Thread myThread = new Thread(){
@Override
public void run() {
super.run();
Message message = handler.obtainMessage();
message.what = 111;
Bundle bundle = new Bundle();
bundle.putString("Content","內(nèi)容");
message.setData(bundle);
handler.sendMessage(message);
}
};
3.源碼相關(guān)解析
消息機(jī)制一般有是三個(gè)主要組成部分:
1.消息發(fā)送者袜炕;
2.消息隊(duì)列锭吨;
3.消息處理循環(huán)
運(yùn)行機(jī)制:
消息發(fā)送者通過某種方式痕寓,將消息發(fā)送到某個(gè)消息隊(duì)列,同時(shí)存在一個(gè)消息處理循環(huán)蝇闭,不斷的從消息隊(duì)列中取出消息進(jìn)行處理呻率。
Android中的Handler消息處理主要由五個(gè)部分組成:
Message:
Message是線程中實(shí)實(shí)在在傳遞的消息,用于線程間交換數(shù)據(jù)呻引,有四個(gè)常用字段礼仗,what、arg1逻悠、arg2藐守、obj,前三者是int型蹂风,用于int型數(shù)據(jù)的傳遞卢厂,好處是輕量,obj可以傳遞稍復(fù)雜的object對象惠啄,但是請注意數(shù)據(jù)量不要太大慎恒;
Handler:
發(fā)送和處理消息,具體是怎么處理的待會兒再講撵渡;
MessageQueue
消息隊(duì)列融柬,存放Handler發(fā)送過來的Message,它是一個(gè)單鏈表結(jié)構(gòu)趋距,消息會被一個(gè)接一個(gè)的處理粒氧,每個(gè)線程只存在一個(gè)消息隊(duì)列
Looper
消息都存在MessageQueue中,怎么取出來呢节腐?Looper中的loop()方法是一個(gè)死循環(huán)外盯,Looper通過調(diào)用這個(gè)方法,只要消息隊(duì)列中存在消息翼雀,就會一個(gè)接一個(gè)的取出來傳遞到Handler的handleMessage()中饱苟,當(dāng)然的每個(gè)線程都只有一個(gè)一個(gè)Looper對象。
ThreadLocal
為了保證每個(gè)線程只有一個(gè)Looper對象狼渊,我們使用ThreadLocal來存儲Looper箱熬,它的內(nèi)部做了并發(fā)的相關(guān)處理,保證Looper的唯一性
通過sendMessage()方法我們找到實(shí)際調(diào)用的是MessageQueue中的
boolean enqueueMessage(Message msg, long when) {
//msg.target是handler對象
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//Message
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//保證并發(fā)安全
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;
//這個(gè)時(shí)候新消息會插入到鏈表的表頭狈邑,隊(duì)列需要調(diào)整喚醒時(shí)間
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 新消息會插入到鏈表的內(nèi)部城须,一般情況下,這不需要調(diào)整喚醒時(shí)間米苹。
// 但還必須考慮到當(dāng)表頭為“同步分割欄”的情況
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
// 說明即便msg是異步的糕伐,也不是鏈表中第一個(gè)異步消息,所以沒必要喚醒
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節(jié)點(diǎn)驱入。鏈表是按照時(shí)間排序的赤炒,這段代碼主要是在比對Message的when信息,消息鏈表的第一個(gè)節(jié)點(diǎn)對應(yīng)著最先被處理的消息亏较,如果Message被插入到鏈表的頭部莺褒,意味著隊(duì)列的最近喚醒時(shí)間也跟著改變,needWake被置為true雪情,進(jìn)入nativeWake方法遵岩。nativeWake()方法對應(yīng)的是c++層,我們暫不做深入研究巡通。
接下來我們看消息循環(huán)Looper的loop()方法尘执。我們知道程序都有相應(yīng)的入口,Activity是怎么樣的消息驅(qū)動(dòng)機(jī)制呢宴凉,在ActivityThread的main方法中我們發(fā)現(xiàn)是調(diào)用了 Looper.prepareMainLooper()創(chuàng)建主線程的Looper和MessageQueue誊锭,并通過 Looper.loop()開啟消息循環(huán)。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
內(nèi)部調(diào)用了prepare(false)和myLooper()
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));
}
sThreadLocal保存了一個(gè)Looper對象弥锄,首先判斷是否已經(jīng)存在Looper對象丧靡,ThreadLocal是并發(fā)安全的,同時(shí)保證么個(gè)線程只有一個(gè)Looper對象籽暇。
我么看一下Looper的構(gòu)造函數(shù)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
創(chuàng)建Looper的同時(shí)創(chuàng)建了一個(gè)MessageQueue消息隊(duì)列和保存了當(dāng)前線程温治,quitAllowed表示消息隊(duì)列是否可以退出。
prepareMainLooper()中調(diào)用的myLooper()是什么呢戒悠?看源碼啟示是從sThreadLocal中取出Looper熬荆。
Looper的初始化完成了,接下來是另一個(gè)重要的方法Looper.loop():
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
邏輯比較簡單绸狐,通過一個(gè)死循環(huán)不斷的從MessageQueue的next()方法中取出消息卤恳,然后調(diào)用msg.target.dispatchMessage(msg),再然后回收該消息寒矿,如果消息為空纬黎,繼續(xù)循環(huán),直到有新消息為止劫窒,這是一個(gè)阻塞操作本今,msg.target上文我們講過就是handler對象。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback是什么呢主巍,點(diǎn)擊查看到Handler的構(gòu)造函數(shù)我們看到callback是作為參數(shù)傳入的冠息,我們在調(diào)用Handler的post方法的時(shí)候傳的是Runnable對象。
我們來看看我們經(jīng)常會用到的兩個(gè)方法runOnUiThread和View的postDelayed
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
return true;
}
可以看到都是執(zhí)行了主線程的run()方法孕索。
講到這里Handler的通訊原理基本上講完了逛艰,但是其實(shí)還有一個(gè)很重要的地方?jīng)]有說到,就是MessageQueue的next()方法搞旭。MessageQueue是怎么一個(gè)接一個(gè)的取消息呢散怖,我們來看:
對消息隊(duì)列而言菇绵,在摘取消息時(shí)還要考慮更多技術(shù)細(xì)節(jié)。
消息隊(duì)列它應(yīng)該具有以下特點(diǎn):
1.當(dāng)消息隊(duì)列中沒有新消息時(shí)镇眷,我們應(yīng)該使之阻塞咬最,而不應(yīng)該繼續(xù)下面的一系列的操作;
2.隊(duì)列里的消息應(yīng)該按照時(shí)間先后順序排列欠动,最先到時(shí)的消息會放在隊(duì)列的頭部永乌,時(shí)間這里指的是mMessage.when,依次排序具伍;
3.阻塞時(shí)間最好能精確翅雏,如果暫時(shí)沒有合適的消息可以摘取時(shí),要考慮鏈表的消息首個(gè)節(jié)點(diǎn)將什么時(shí)間到人芽,這個(gè)消息節(jié)點(diǎn)到當(dāng)前的時(shí)間差望几,就是我們要阻塞的時(shí)間;
4.有些時(shí)候我們可能希望在消息隊(duì)列進(jìn)入阻塞的時(shí)候做一些動(dòng)作萤厅,這些動(dòng)作稱為idle動(dòng)作橄妆,我們需要兼顧處理這些idle動(dòng)作。
MessageQueue的next()函數(shù)如下:
Message next()
{
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
. . . . . .
nativePollOnce(mPtr, nextPollTimeoutMillis); // 阻塞于此
. . . . . .
// 獲取next消息祈坠,如能得到就返回之害碾。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; // 先嘗試拿消息隊(duì)列里當(dāng)前第一個(gè)消息
if (msg != null && msg.target == null) {
// 如果從隊(duì)列里拿到的msg是個(gè)“同步分割欄”,那么就尋找其后第一個(gè)“異步消息”
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.
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; // 重新設(shè)置一下消息隊(duì)列的頭部
}
msg.next = null;
if (false) Log.v("MessageQueue", "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 (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;
}
. . . . . .
// 處理idle handlers部分
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("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
當(dāng)MessageQueue的定時(shí)機(jī)制觸發(fā)后赦拘,會判斷這條消息是否真的到時(shí)慌随,如果到時(shí),則直接返回這個(gè)Message躺同,如果沒有到時(shí)阁猜,則計(jì)算一個(gè)等待時(shí)間,繼續(xù)for循環(huán)繼續(xù)調(diào)用nativePollOnce(mPtr, nextPollTimeoutMillis)蹋艺,進(jìn)入阻塞狀態(tài)剃袍,等待指定的等待時(shí)間。
next()中的Idle Handler捎谨,我們前面說過民效,在消息隊(duì)列阻塞之前,我們可能希望做一些其它的操作涛救,比如垃圾回收畏邢,在ActivityThread中我們看一下GcIdler的定義:
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
Handler的消息機(jī)制我們講完了,還有一些細(xì)節(jié)我們沒有涉及检吆,比如我們發(fā)現(xiàn)源碼里面調(diào)用了native層的方法舒萎、比如同步分隔欄的原理,如果你想繼續(xù)深入的研究下去蹭沛,你可以去深究這些代碼臂寝,下面的參考博文章鲤,講的很詳細(xì),其實(shí)我也是查看源碼咆贬,然后有一些不懂的地方去對照著這篇博文去理解败徊,這里感謝這篇文章的博主。
參考: