一、Handler原理
Handler 是 Android 中線(xiàn)程間通信的組件岛都。在異步線(xiàn)程中使用前需要先調(diào)用 Looper.prepare 為當(dāng)前線(xiàn)程準(zhǔn)備 Looper 和 Looper 持有的消息隊(duì)列律姨,然后通過(guò)創(chuàng)建的 Handler 對(duì)象像這個(gè)消息隊(duì)列里插入消息,調(diào)用 Looper.loop 方法啟動(dòng) loop 循環(huán)處理消息臼疫。
創(chuàng)建 Looper 對(duì)象
當(dāng)調(diào)用 Looper.perpare 函數(shù)時(shí)择份,在 Java 層會(huì)為當(dāng)前線(xiàn)程創(chuàng)建一個(gè) Looper 以及 MessageQueue 對(duì)象。創(chuàng)建 MessageQueue 時(shí),同時(shí)還會(huì)調(diào)用 nativeInit 函數(shù)去創(chuàng)建 native 層的 MessageQueue 和 Looper 對(duì)象烫堤。Native 層的 Looper 對(duì)象會(huì)去創(chuàng)建一個(gè) EventFd 對(duì)象荣赶。它是消息循環(huán)的核心凤价。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
創(chuàng)建 Handler 對(duì)象
- 首先判斷是否為當(dāng)前線(xiàn)程準(zhǔn)備了 Looper 對(duì)象。如果沒(méi)有會(huì)拋出異常
- 通過(guò)調(diào)用 Looper 的 prepare 方法準(zhǔn)備當(dāng)前線(xiàn)程環(huán)境的 Looper 對(duì)象
- 獲取 Looper 中維護(hù)的消息隊(duì)列
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 隊(duì)列中插入消息
- 在將消息入隊(duì)前對(duì)樣會(huì)把自身的引用賦值給這個(gè) Message讯壶,在取出消息時(shí)就可以方便的知道消息是由哪一個(gè) 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 插入消息時(shí)會(huì)對(duì)消息進(jìn)行排序
- 如果當(dāng)前隊(duì)列沒(méi)有消息料仗,新消息的延時(shí)為0,或者新消息的延時(shí)時(shí)間要比當(dāng)前消隊(duì)列第一個(gè)的延時(shí)時(shí)間短伏蚊,都會(huì)將這個(gè)新的消息作為第一個(gè)消息優(yōu)先被取出
- 否則就將消息消息鏈接在之前的消息后面
- 如果之前的 Looper 在等待狀態(tài)會(huì)喚起 Looper
喚醒消息是通過(guò)調(diào)用 nativeWake 函數(shù)立轧,它會(huì)調(diào)用 native looper 對(duì)象的 wake 函數(shù)向 EventFD 寫(xiě)入一個(gè)數(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 中寫(xiě)入一個(gè)數(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 寫(xiě)入了一個(gè)數(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 通過(guò)調(diào)用 MessageQueue 的 next 函數(shù)取出消息
- 通過(guò)消息中持有的 Handler 引用觸發(fā)對(duì)應(yīng) Handler 的 dispatchMessage 執(zhí)行對(duì)應(yīng)的任務(wù)
public static void loop() {
final MessageQueue queue = me.mQueue;
for (;;) {
// 如果沒(méi)有消息要發(fā)送會(huì)讓出 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í)氛改,會(huì)判斷隊(duì)列中是否有消息,如果沒(méi)有消息比伏,會(huì)將延時(shí)時(shí)間設(shè)置成 -1胜卤,再次循環(huán)時(shí)會(huì)調(diào)用 nativePollOnce 函數(shù)進(jìn)入無(wú)限的休眠期。nativePollOnce 函數(shù)本質(zhì)上是調(diào)用 native Looper 對(duì)象的 pollOnce 函數(shù)通過(guò)調(diào)用 epoll_wait 阻塞在當(dāng)前的位置赁项。
- 如果當(dāng)前有消息葛躏,會(huì)取出消息計(jì)算它的發(fā)送時(shí)間,如果消息的時(shí)間比當(dāng)前時(shí)間小就立即發(fā)送悠菜,如果消息時(shí)間比當(dāng)前的時(shí)間大就計(jì)算延時(shí)的時(shí)間舰攒,進(jìn)入休眠。等待休眠時(shí)間到以后再發(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
- 如果沒(méi)有就判斷是否有全局的 callback
- 如果沒(méi)有全局的 callback 就調(diào)用重寫(xiě)后的 handleMessage 方法悔醋。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
二摩窃、常見(jiàn)問(wèn)題
1. Handler 延時(shí)消息的原理
Handler 消息的延時(shí)是處理延時(shí),任何發(fā)送的消息都是第一時(shí)間入隊(duì)的芬骄,在發(fā)送消息時(shí)會(huì)和當(dāng)前時(shí)間做比較猾愿,如果需要延時(shí)就會(huì)計(jì)算延時(shí)時(shí)間,調(diào)用 epoll_wait 阻塞住账阻,等待延時(shí)時(shí)間達(dá)到后喚醒當(dāng)前的線(xiàn)程發(fā)送消息
2. IdleHandler原理
IdleHandler 可以在當(dāng)前線(xiàn)程的消息隊(duì)列空閑時(shí)做一些事情蒂秘。它的原理是向 IdleHandler 的隊(duì)列中插入了一個(gè)消息。當(dāng) MessageQueue 去查找隊(duì)里中的消息時(shí)淘太,如果隊(duì)列中沒(méi)有消息或者消息還沒(méi)有達(dá)到發(fā)送的時(shí)間材彪,就會(huì)執(zhí)行 IdleHandler 隊(duì)列中的消息
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 可以做一些延時(shí)初始化的任務(wù)。
- 當(dāng)一個(gè) View 頻繁接受消息并刷新時(shí)琴儿,可以使用 IdleHandler段化,讓任務(wù)隊(duì)里先去處理消息,等到線(xiàn)程空閑時(shí)使用 IdleHandler 去更新最新的數(shù)據(jù)造成。
3. 子線(xiàn)程和主線(xiàn)程 Looper 的區(qū)別
子線(xiàn)程的 Looper 可以退出而主線(xiàn)程的是不可以的显熏。
**4. 應(yīng)用線(xiàn)程進(jìn)入 looper 循環(huán)為什么沒(méi)有 ANR **
ANR 是 Android 中的一種機(jī)制,它是在應(yīng)用沒(méi)有按時(shí)完成 AMS 指定的任務(wù)才觸發(fā)的晒屎。組件在創(chuàng)建時(shí)會(huì)向 AMS 申請(qǐng)開(kāi)始計(jì)時(shí)喘蟆,當(dāng)完成創(chuàng)建后會(huì)通知 AMS 取消計(jì)時(shí)缓升。
進(jìn)入 looper 循環(huán)后,AMS 會(huì)在 looper 線(xiàn)程中通過(guò)主線(xiàn)程的 Handler 發(fā)送消息給主線(xiàn)程去執(zhí)行任務(wù)。所以即使進(jìn)入 looper 循環(huán), AMS 仍然可以和主線(xiàn)程交互蕴轨。這也是為什么 ApplicationThread 接到任務(wù)后還需要發(fā)送 Handler 消息給 ActivityThread 去執(zhí)行任務(wù)港谊。
如果主線(xiàn)程中有其他任務(wù)導(dǎo)致 AMS 的任務(wù)被延時(shí),或者 AMS 的任務(wù)本身很耗時(shí)才會(huì)觸發(fā) ANR
5.消息屏障
消息屏障是 Handler 優(yōu)先執(zhí)行異步消息的一種機(jī)制橙弱。在 android 中 choreographer 類(lèi)在刷新 view 時(shí)使用到了
在子線(xiàn)程中setText可能成功么
在setTextView的時(shí)候會(huì)調(diào)用requestLayout會(huì)做線(xiàn)程檢查,如果不是的主線(xiàn)程會(huì)拋出異常歧寺,但是不是絕對(duì)的,如果invalid 比 requestLayout執(zhí)行快,那么不會(huì)拋出異常棘脐。
創(chuàng)建Handler時(shí)傳入Callback有什么影響
Handler在取消息時(shí)會(huì)優(yōu)先查找是否設(shè)置了callback(通過(guò)msg設(shè)置callback,或者通過(guò)Handler構(gòu)造方法直接傳入),性能會(huì)更好
為什么post一個(gè)runnable原理(如何讓handleMessage快速執(zhí)行)
使用handler直接post一個(gè)runnable對(duì)象,這個(gè)runnbale對(duì)象會(huì)被包裝成一個(gè)message對(duì)象,這個(gè)runable會(huì)作為message的callback方法斜筐,結(jié)合上面來(lái)看如果message有callback那么會(huì)不經(jīng)過(guò)判斷執(zhí)行執(zhí)行handleMessage方法。
一直死循環(huán)不會(huì)造成cpu浪費(fèi)么
在沒(méi)有消息的時(shí)候,會(huì)阻塞在nativePollOnce方法上蛀缝,讓出cpu資源顷链,進(jìn)入休眠狀態(tài),當(dāng)有新的任務(wù)進(jìn)入時(shí)會(huì)重新喚醒cpu