Handler机制原理图
常见问题:
1.一个线程可以有多少个Handler?
答:可以有无数个,因为我们可以无限的去创建Handler
2.一个线程有几个Looper?如何保证?
答:一个线程只能有一个Looper,例如主线程在ThreadActivity的main函数中,调用 Looper.prepareMainLooper(),初始化Looper,将ThreadLocal对象和Looper存入ThreadLocalMap中,确保每个线程只有一个looper对象
//Looper 的prepare函数 private static void prepare(boolean quitAllowed) {
// 当同一个线程多次调用prepare方法时,就会抛出异常 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); }
// 将Looper对象存入ThreadLocalMap中 sThreadLocal.set(new Looper(quitAllowed)); }
3.Handler作为内部类使用会内存泄漏?为什么其他内部类不会?
内部类持有外部类的引用,handler.sendMessage(msg)-->enqueueMessage(),假如某一时刻外部类即将被销毁,而handler还没有处理完消息,msg持有handler,handler持有外部类,导致外部类无法被回收,进而导致内存泄漏;其他内部类不会是因为其他内部类没有延时操作。
// Handler的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 将msg和Handler绑定,以便消息处理时候,可以找到正确的handler
msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); }
// 将msg加入到消息队列 return queue.enqueueMessage(msg, uptimeMillis); }
4.为何主线程可以new Handler?如何在子线程中new Handler?
答:主线程中可以直接new是因为主线程在ThreadActivity的main函数中已经做了Looper初始化,并调用了loop()方法,可以使消息处理机制运行起来;倘若想要在子线程中new Handler,需要自己调用Looper的prepare()和Loop()方法,如果还想退出,可以调用Looper的quit()函数。
5.子线程中维护L
6.既然可以存在多个handler,往MessageQueue中添加数据发消息时怎么保证消息不混乱?
答:往消息队列中添加消息时,enqueueMessage()使用同步代码块,将添加操作锁住
7.我们在使用Message的时候怎么创建它;
答:使用静态方法obtain(),获取消息,消息队列默认的长度是50,可以回收利用
8.使用handler的postDelay后,消息队列会有什么变化?
答:如果没有消息会计算nextPollTimeoutMillis,进入nativePollOnce休眠,等待时间,然后取出消息,回调handleMessage。
9.Looper死循环为什么不会导致应用卡死?
答:每个app都有一个自己的虚拟机,点击图标流程:launcher-->application-->zygote-->虚拟机-->ActivityThread,执行Looper的loop方法,会有一个死循环,会调用queue.next();调用next(), nativePollOnce(ptr, nextPollTimeoutMillis); 如果没有消息处理,会使线程休眠,不至于一直循环,等有消息时再去唤醒,使loop方法继续执行。
for (;;) {
// 这行代码会使线程处于休眠状态 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } 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); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); }
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();
}
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;
}
// 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;
}
}