一、Handler原理
Handler 是 Android 中線程間通信的組件。在異步線程中使用前需要先調(diào)用 Looper.prepare 為當(dāng)前線程準(zhǔn)備 Looper 和 Looper 持有的消息隊列,然后通過創(chuàng)建的 Handler 對象像這個消息隊列里插入消息,調(diào)用 Looper.loop 方法啟動 loop 循環(huán)處理消息。
創(chuàng)建 Looper 對象
當(dāng)調(diào)用 Looper.perpare 函數(shù)時,在 Java 層會為當(dāng)前線程創(chuàng)建一個 Looper 以及 MessageQueue 對象。創(chuàng)建 MessageQueue 時,同時還會調(diào)用 nativeInit 函數(shù)去創(chuàng)建 native 層的 MessageQueue 和 Looper 對象。Native 層的 Looper 對象會去創(chuàng)建一個 EventFd 對象。它是消息循環(huán)的核心。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
創(chuàng)建 Handler 對象
- 首先判斷是否為當(dāng)前線程準(zhǔn)備了 Looper 對象。如果沒有會拋出異常
- 通過調(diào)用 Looper 的 prepare 方法準(zhǔn)備當(dāng)前線程環(huán)境的 Looper 對象
- 獲取 Looper 中維護的消息隊列
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
public Handler(Callback callback, boolean async) {
....
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2.發(fā)送消息
- 發(fā)送消息本質(zhì)上是像 Looper 所持有的 MessageQueue 隊列中插入消息
- 在將消息入隊前對樣會把自身的引用賦值給這個 Message,在取出消息時就可以方便的知道消息是由哪一個 Handler 發(fā)送的
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
......
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue 插入消息時會對消息進(jìn)行排序
- 如果當(dāng)前隊列沒有消息,新消息的延時為0,或者新消息的延時時間要比當(dāng)前消隊列第一個的延時時間短,都會將這個新的消息作為第一個消息優(yōu)先被取出
- 否則就將消息消息鏈接在之前的消息后面
- 如果之前的 Looper 在等待狀態(tài)會喚起 Looper
喚醒消息是通過調(diào)用 nativeWake 函數(shù),它會調(diào)用 native looper 對象的 wake 函數(shù)向 EventFD 寫入一個數(shù)字,喚醒被 epoll_wait 阻塞的代碼
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();
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.
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;
}
// 向 EventFD 中寫入一個數(shù)字,喚醒 epoll_wait 阻塞的地方
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
JNI 層
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
核心就是向 mWakeEventFd 寫入了一個數(shù)字 write(mWakeEventFd.get()
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
3. Looper 取出消息
- Looper 通過調(diào)用 MessageQueue 的 next 函數(shù)取出消息
- 通過消息中持有的 Handler 引用觸發(fā)對應(yīng) Handler 的 dispatchMessage 執(zhí)行對應(yīng)的任務(wù)
public static void loop() {
final MessageQueue queue = me.mQueue;
for (;;) {
// 如果沒有消息要發(fā)送會讓出 CPU
Message msg = queue.next();
.....
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
MessageQueue 的 next 方法
- 第一次進(jìn)入時,會判斷隊列中是否有消息,如果沒有消息,會將延時時間設(shè)置成 -1,再次循環(huán)時會調(diào)用 nativePollOnce 函數(shù)進(jìn)入無限的休眠期。nativePollOnce 函數(shù)本質(zhì)上是調(diào)用 native Looper 對象的 pollOnce 函數(shù)通過調(diào)用 epoll_wait 阻塞在當(dāng)前的位置。
- 如果當(dāng)前有消息,會取出消息計算它的發(fā)送時間,如果消息的時間比當(dāng)前時間小就立即發(fā)送,如果消息時間比當(dāng)前的時間大就計算延時的時間,進(jìn)入休眠。等待休眠時間到以后再發(fā)送
Message next() {
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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.
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;
}
}
native 的 Looper
.....
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
.....
4.處理消息結(jié)果
Handler 的 handleMessage 方法
- 首先判斷 Message 是否指定了 callback
- 如果沒有就判斷是否有全局的 callback
- 如果沒有全局的 callback 就調(diào)用重寫后的 handleMessage 方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
二、常見問題
1. Handler 延時消息的原理
Handler 消息的延時是處理延時,任何發(fā)送的消息都是第一時間入隊的,在發(fā)送消息時會和當(dāng)前時間做比較,如果需要延時就會計算延時時間,調(diào)用 epoll_wait 阻塞住,等待延時時間達(dá)到后喚醒當(dāng)前的線程發(fā)送消息
2. IdleHandler原理
IdleHandler 可以在當(dāng)前線程的消息隊列空閑時做一些事情。它的原理是向 IdleHandler 的隊列中插入了一個消息。當(dāng) MessageQueue 去查找隊里中的消息時,如果隊列中沒有消息或者消息還沒有達(dá)到發(fā)送的時間,就會執(zhí)行 IdleHandler 隊列中的消息
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);
}
}
}
- IdleHandler 可以做一些延時初始化的任務(wù)。
- 當(dāng)一個 View 頻繁接受消息并刷新時,可以使用 IdleHandler,讓任務(wù)隊里先去處理消息,等到線程空閑時使用 IdleHandler 去更新最新的數(shù)據(jù)。
3. 子線程和主線程 Looper 的區(qū)別
子線程的 Looper 可以退出而主線程的是不可以的。
**4. 應(yīng)用線程進(jìn)入 looper 循環(huán)為什么沒有 ANR **
ANR 是 Android 中的一種機制,它是在應(yīng)用沒有按時完成 AMS 指定的任務(wù)才觸發(fā)的。組件在創(chuàng)建時會向 AMS 申請開始計時,當(dāng)完成創(chuàng)建后會通知 AMS 取消計時。
進(jìn)入 looper 循環(huán)后,AMS 會在 looper 線程中通過主線程的 Handler 發(fā)送消息給主線程去執(zhí)行任務(wù)。所以即使進(jìn)入 looper 循環(huán), AMS 仍然可以和主線程交互。這也是為什么 ApplicationThread 接到任務(wù)后還需要發(fā)送 Handler 消息給 ActivityThread 去執(zhí)行任務(wù)。
如果主線程中有其他任務(wù)導(dǎo)致 AMS 的任務(wù)被延時,或者 AMS 的任務(wù)本身很耗時才會觸發(fā) ANR
5.消息屏障
消息屏障是 Handler 優(yōu)先執(zhí)行異步消息的一種機制。在 android 中 choreographer 類在刷新 view 時使用到了
在子線程中setText可能成功么
在setTextView的時候會調(diào)用requestLayout會做線程檢查,如果不是的主線程會拋出異常,但是不是絕對的,如果invalid 比 requestLayout執(zhí)行快,那么不會拋出異常。
創(chuàng)建Handler時傳入Callback有什么影響
Handler在取消息時會優(yōu)先查找是否設(shè)置了callback(通過msg設(shè)置callback,或者通過Handler構(gòu)造方法直接傳入),性能會更好

為什么post一個runnable原理(如何讓handleMessage快速執(zhí)行)
使用handler直接post一個runnable對象,這個runnbale對象會被包裝成一個message對象,這個runable會作為message的callback方法,結(jié)合上面來看如果message有callback那么會不經(jīng)過判斷執(zhí)行執(zhí)行handleMessage方法。
一直死循環(huán)不會造成cpu浪費么
在沒有消息的時候,會阻塞在nativePollOnce方法上,讓出cpu資源,進(jìn)入休眠狀態(tài),當(dāng)有新的任務(wù)進(jìn)入時會重新喚醒cpu