[toc]
問題1:Handler機制是怎么實現(xiàn)線程間通信的?
問題2:post(runnable)方法發(fā)送的runnable為什么能在主線程執(zhí)行?
創(chuàng)建
首先,看一下創(chuàng)建Handler的過程:
//Handler.java
final Looper mLooper;
final MessageQueue mQueue;
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
...
mLooper = Looper.myLooper(); //獲取當(dāng)前線程的looper實例
...
mQueue = mLooper.mQueue; //當(dāng)前線程的消息隊列
mCallback = callback; //用于處理消息
mAsynchronous = async;
}
在Handler的構(gòu)建方法中,主要是字段賦值,通過Looper.myLooper()獲取了Looper實例,接下來就看一下Looper.myLooper()的源碼。
//Looper.java
//ThreadLocal用于線程局部變量,值與調(diào)用線程關(guān)聯(lián)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* 返回與當(dāng)前線程關(guān)聯(lián)的Looper對象。如果調(diào)用線程沒有與循環(huán)器關(guān)聯(lián),則返回null。
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
...
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
注:ThreadLocal類型用于線程局部變量,它的值將保存在thread.threadLocals字段,get/set時通過Thread.currentThread()方法獲取當(dāng)前線程thread實例來訪問。
Looper.myLooper()返回的looper來自靜態(tài)字段sThreadLocal,而sThreadLocal的值是在Looper.prepare()方法中創(chuàng)建的looper實例。(Looper.prepare()并不是在創(chuàng)建handler時調(diào)用,而是在之前調(diào)用)
- 主線程已在
ActivityThread.main()方法中調(diào)用了Looper.prepareMainLooper()及Looper.loop(),無需我們負(fù)責(zé); - 在其他線程創(chuàng)建Handler時,必須在
new Handler()前,調(diào)用Looper.prepare(),否則會引發(fā)RuntimeException。
分析到這里,可以知道handler.mLooper引用的looper是和線程關(guān)聯(lián)的,是線程局部變量,在Looper中創(chuàng)建的消息隊列自然也和線程關(guān)聯(lián)。所以Handler在創(chuàng)建時便和線程綁定了,由handler發(fā)送的消息,才能在創(chuàng)建handler的線程處理。
發(fā)送消息
handler最常見的用法是sendMessage(msg)和post(runnable),先看handler.sendMessage()方法的源碼:
//Handler.java
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
//SystemClock.uptimeMillis()返回開機后的毫秒數(shù)
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//參數(shù)uptimeMillis指定處理消息的時間
public boolean sendMessageAtTime(@NonNull 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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this; //消息發(fā)送的目標(biāo),表示消息將投遞到當(dāng)前handler處理
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); //將消息放入隊列
}
經(jīng)過幾層調(diào)用,添加了預(yù)定處理時間,設(shè)置了發(fā)送目標(biāo)后,消息被放入handler.mQueue,之前已經(jīng)分析過這個消息隊列和創(chuàng)建handler的線程綁定。
另外在handler.enqueueMessage()中設(shè)置了msg.target = this,這說明發(fā)送消息和處理消息一定是同一個handler。
然后看一下queue.enqueueMessage()方法。
//MessageQueue.java
private long mPtr; // 大概相當(dāng)于native層的線程id
Message mMessages; //鏈表:保存等待處理的消息
private boolean mBlocked; //指示消息隊列關(guān)聯(lián)的線程是否阻塞
private boolean mQuitting; //消息隊列是否已廢棄
boolean enqueueMessage(Message msg, long when) {
...
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(); //回收message對象,保存至Message.sPool
return false;
}
msg.markInUse();
msg.when = when; //消息預(yù)設(shè)的投遞時間
Message p = mMessages;
boolean needWake; //是否需要喚醒線程
//根據(jù)when決定消息插入隊列的位置
if (p == null || when == 0 || when < p.when) {
// 新的頭,如果阻塞,喚醒事件隊列。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 插入到隊列中間。通常我們不需要喚醒事件隊列,
// 除非隊列前面有一個屏障,并且消息是隊列中最早的異步消息。
// p.target == null代表一個‘同步屏障’消息,它之后的同步消息暫時不投遞
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;
}
MessageQueue.enqueueMessage()的主要工作是根據(jù)參數(shù)when將消息插入隊列適當(dāng)位置,判斷線程是否阻塞,是否需要喚醒線程。
總結(jié)一下:
-
handler.sendMessage(msg)的過程,就是將msg放入handler.mQueue.mMessags。 - 消息將被 發(fā)送它的handler 處理
下面看一下handler.post()方法的源碼:
//Handler.java
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
很明顯,post()方法只是生成一個message對象,然后通過sendMessage...()方法發(fā)送。不同的是這里設(shè)置了message.callback字段,另外使用過handler.post()一定知道,這些消息不會通過handler.handleMessage()處理,那么post()產(chǎn)生的消息會怎么處理呢?我們在“分發(fā)消息”部分分析。
分發(fā)消息
消息隊列中的消息是在Looper.loop()方法中分發(fā)的,直接看源碼。
//Looper.java
/**
* 在此線程中運行消息隊列。一定要調(diào)用quit()來結(jié)束循環(huán)。
*/
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); //從消息隊列取出消息。(可能阻塞)
//正常時候,隊列中沒有消息,會阻塞線程,不會返回null
if (msg == null) {
// 沒有消息 表示消息隊列正在退出。
return;
}
...
// 確保觀察者在處理事務(wù)時不會改變。
final Observer observer = sObserver;
...
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
...
try {
//將消息投遞到handler。(msg.target為發(fā)送消息的handler)
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
...
}
...
msg.recycleUnchecked(); //回收msg實例
}
}
主要邏輯很簡單,就是從消息隊列取出消息,投遞給指定的handler,然后回收消息。所以在這里只是把消息分發(fā)到了handler,然后再看handler.dispatchMessage(msg)怎么分發(fā)消息:
//Handler.java
@UnsupportedAppUsage
final Callback mCallback; //創(chuàng)建handler時設(shè)置,用于處理消息
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//post(runnable)產(chǎn)生的消息,在此處理
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run(); //message.callback為Runnable
}
/**
* 子類必須實現(xiàn)此方法以接收消息。
*/
public void handleMessage(@NonNull Message msg) {
}
/**
* 創(chuàng)建Handler實例時使用,避免實現(xiàn)Handler子類
*/
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
handler.dispatchMessage(msg)的邏輯很簡單,按照以下優(yōu)先級分發(fā)消息 msg.callback > mCallback > handleMessage()
-
handleCallback()直接運行message.callback -
handleMessage()需要在子類中實現(xiàn) -
handler.mCallback只有一個方法,功能與handler.handleMessage()相同,只是省得寫Handler子類。(感覺沒什么用)
在Looper.loop()中調(diào)用了MessageQueue.next(),這里也看一下:
//MessageQueue.java
private long mPtr; // 大概相當(dāng)于native層的線程id
Message mMessages; //鏈表:保存等待處理的消息
private boolean mBlocked; //指示消息隊列關(guān)聯(lián)的線程是否阻塞
private boolean mQuitting; //消息隊列是否已廢棄
@UnsupportedAppUsage
Message next() {
...
int nextPollTimeoutMillis = 0; //預(yù)設(shè)阻塞時長
for (;;) { //注意:沒找到可返回的消息時,會循環(huán)
...
//使線程阻塞,nextPollTimeoutMillis為預(yù)設(shè)阻塞最長時間,可以提前喚醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 嘗試檢索下一條消息。如果找到則返回。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//msg.target==null代表一個‘同步屏障’消息,它之后的同步消息暫時不投遞
// 隊列頭是同步屏障,則查找下一個異步消息。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果還沒有到消息預(yù)設(shè)時間,設(shè)置阻塞時間,下一次循環(huán)時阻塞
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 {
// 沒有消息,使線程一直阻塞。enqueueMessage()中有喚醒邏輯
nextPollTimeoutMillis = -1;
}
// 現(xiàn)在處理退出消息,所有掛起的消息都已處理。
if (mQuitting) {
dispose();
return null;
}
...
}
...
}
}
總結(jié)
- 在主線程創(chuàng)建handler實例后,可以在不同線程使用這個handler實例發(fā)送消息,這些消息都會放到主線程的消息隊列中,而主線程在
Looper.loop()方法中不斷地取出消息處理消息。 - 主線程調(diào)用
Looper.loop()后會無限循環(huán),那么Activity生命周期方法、屏幕刷新、點擊事件等只能在處理消息時執(zhí)行。 - 在我們自定義的線程也可以使用Looper,使線程長久運行。
參考
- 源碼:andorid-29
- 深入理解MessageQueue