1、Handler 简介
Handler 是一套 Android 消息传递机制。
在多线程应用场景中,将子线程中需要更新 UI 的操作消息,传递到 UI 主线程,从而实现子线程通知 UI 更新最终实现异步消息处理。说白了是用于线程之间的通信。
Handler主要有4个重要类:Handler、Message、MessageQueue、Looper。
- Handler:负责消息的发送和处理,子线程中使用 sendMessage() 发送消息;在handleMessage()中处理。
- Message:消息载体,里面存储这线程消息。
- MessageQueue:消息队列,遵循先进先出的原则,存储着 sendMessage() 发送来的子线程消息。
- Looper:消息循环器,负责从 MessageQueue 中循环取消息,再将取出的消息分发给
handleMessage(),来处理消息。
Handler原理图.png
3、Handler 源码
了解 Handler,首先我们要了解 Handler 从消息发送到消息处理这整个流程,下面将分析这一流程,并回答下面几个问题:
- 1、一个线程有几个Handler?
- 2、一个线程有几个Looper?如何保证?
- 3、Handler 内存泄漏原因?为什么其他的内部分没有过这个问题?
- 4、为何主线程可以 new Handler?如果想要在子线程中 new Handler 要做些什么准备?
- 5、子线程中的维护的 looper,消息队列无消息的时候处理方案是什么?有什么用?
- 6、既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时,各个handler 可能处于不同的线程),那么它内部是如何确保线程安全的?
- 7、我们使用 Message 时应如何创建它?
- 8、使用 Handler 的 postDelay 后,消息队列会有什么变化?
- 9、Looper 死循环为什么不会导致应用卡死(ANR)?
1. Handler
Handler 负责消息的发送和处理,该类中通过 sendXXX、postXXX等方法发送消息,共有14个这样的方法。而在 handleMessage() 中处理收到的消息。
这里以 sendMessage(Message msg)
方法为例进行源码分析。
// Handler.java public class Handler { 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; } // 第二的参数代表执行的时间,为:系统当前时间+延迟的时间 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } 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); } // 不管使用什么方法发送消息,都会调到 Handler#enqueueMessage() private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { // 把当前对象赋给msg.target,这样 Message 就持有了 Handler msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } // 调用 MessageQueue#enqueueMessage() 往消息队列添加消息。 return queue.enqueueMessage(msg, uptimeMillis); } }
1. MessageQueue
enqueueMessage 里面其实是一个优先级队列,将收到消息根据执行的时间 when 进行做排序处理。
// MessageQueue.java public final class MessageQueue { boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } 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; // 如果是0,则放在最前面 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; // 对单链表轮询,根据 when 进行排序插入消息。 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; } }
到这里发消息基本完成,后面看如何取消息。
next() 中返回一个 Message。
// MessageQueue.java public final class MessageQueue { 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(); } // nextPollTimeoutMillis :-1 表示无限等待,直到有事件为止;0 表示立即执行;其他数字表示等待多时毫秒。 // linux 层休眠等待, 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 { // 到了可以执行的时间,则把消息 return 出去。 // 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; } } }
到此,消息取出来了,但是谁取的呢?这就涉及到另一个重要的类 Looper
3. Looper
Looper.loop() 里面会有个for循环,且是个死循环,会不断的调用 MessageQueue#next() 方法。
public final class Looper { // 初始化Looper public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { // 如果该线程有 Looper,则抛出一个异常。 throw new RuntimeException("Only one Looper may be created per thread"); } // 这里使用了 ThreadLocal,保证了一个线程只有一个Looper。 sThreadLocal.set(new Looper(quitAllowed)); } public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } if (me.mInLoop) { Slog.w(TAG, "Loop again would have the queued messages be executed" + " before this one completed."); } me.mInLoop = true; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); me.mSlowDeliveryDetected = false; for (;;) { // 死循环 // 不断地调用mQueue.next() if (!loopOnce(me, ident, thresholdOverride)) { return; } } } private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { // 这里如果此时队列中没有消息或队列中,第一个节点还没到可以执行的时刻,则会进入等待,block 状态。 // 会一直在这等,该等待是Linux层做的,在 mQueue.next()中 Message msg = me.mQueue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return false; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; // Make sure the observer won't change while processing a transaction. final Observer observer = sObserver; 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; Object token = null; if (observer != null) { token = observer.messageDispatchStarting(); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { // 获取到消息后,就会调用 msg.target.dispatchMessage(msg),即回调 handler#dispatchMessage(msg) // msg.target 为 handler对象 msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (me.mSlowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); me.mSlowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. me.mSlowDeliveryDetected = 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(); return true; } }
当获取到消息时,回调用 msg.target.dispatchMessage(msg)
即 handler#dispatchMessage(msg)
,在 dispatchMessage(msg)
中再回调 handleMessage(msg)
,这样就收到消息。至此发消息、收消息整个流程结束。
下面回答上述的问题。
1、一个线程有几个Handler?
答:那个,new 多少就有多少。
2、一个线程有几个Looper?如何保证?
答:一个,在初始化时,使用了 ThreadLocal ,而 ThreadLocal 是一个 <key,value> 这种形式的变量,类似 hashMap。它的 key 是当前线程,value 是 Looper。
下方为对应源码分析:
// 初始化Looper public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { // 如果该线程有 Looper,则抛出一个异常。 throw new RuntimeException("Only one Looper may be created per thread"); } // 这里使用了 ThreadLocal,保证了一个线程只有一个Looper。 sThreadLocal.set(new Looper(quitAllowed)); } // ThreadLocal 的 set() 方法 public void set(T value) { Thread t = Thread.currentThread(); // 当前线程 ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } // ThreadLocal 的 get()方法, public T get() { Thread t = Thread.currentThread(); // 当前线程 ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
3、Handler 内存泄漏原因?为什么其他的内部分没有过这个问题?
出现内存泄漏的情况:Activity 销毁时,存在待处理的消息。例如:发送一个delay(延迟消息) 2s,在2s内销毁界面。
答:Handler 持有 Activity 的上下文,而 MessageQueue 持有 Message,Message 又持有 Handler;只有当这消息被处理时,才会去销毁对应的 Handler ,Handler 被销毁了,才会去销毁持有的上下文。而其他内部类,例如:RecyclerView 的 ViewHolder,不会产生内存泄漏,因为它没有被其它地方持有该内部类。
// Handler.java public class Handler { // 不管使用什么方法发送消息,都会调到 Handler#enqueueMessage() private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { // 把当前对象赋给msg.target,这样 Message 就持有了 Handler msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } // 调用 MessageQueue#enqueueMessage() 往消息队列添加消息。 return queue.enqueueMessage(msg, uptimeMillis); } }
4、为何主线程可以 new Handler?如果想要在子线程中 new Handler 要做些什么准备?
答:主线程在创建时,系统 ActivityThread 就已经创建好了。在子线程中 new Handler 需想初始化 Looper(Looper.prepare()),并启动 loop(Looper.loop())
// ActivityThread.java public static void main(String[] args) { // 省略部分代码...... Looper.prepareMainLooper(); long startSeq = 0; if (args != null) { for (int i = args.length - 1; i >= 0; --i) { if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { startSeq = Long.parseLong( args[i].substring(PROC_START_SEQ_IDENT.length())); } } } ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); /// M: ANR Debug Mechanism mAnrAppManager.setMessageLogger(Looper.myLooper()); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
5、子线程中的维护的 looper,消息队列无消息的时候处理方案是什么?有什么用?
答:在适当地方调用 Looper.quitSafely();安全地退出 looper。 等所有剩余的消息处理完毕后立即终止。但是,在 loop 循环终止之前,将不会在收到消息。在要求循环程序退出后,任何向队列发送消息的尝试都将失败。
6、既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时,各个handler 可能处于不同的线程),那么它内部是如何确保线程安全的?
答:在 MessageQueue#enqueueMessage、MessageQueue#next() 中的代码块使用了 synchronized 修饰。则也会导致 handler 的 delay 消息的时间不完全的准确。
7、我们使用 Message 时应如何创建它?
答:obtain(),避免了每次去 new ,防止了内存抖动。
8、使用 Handler 的 postDelay 后,消息队列会有什么变化?
答:若此时消息队列为空,则不会立马执行(Delay 消息);当该消息添加进去时,MessageQueue#enqueueMessage 会 调用 nativeWake(mPtr) 唤醒消息队列,就会在 MessageQueue#next() 中,计算等待时间。
9、Looper 死循环为什么不会导致应用卡死(ANR)?
每一个事件都是一个 Message,因为所有事件都在Activity的生命周期里面,而主线程的所有代码都运行在 ActivityThread#main()中的 loop 里面。所以主线程的 loop 不能退出。
主线程唤醒的方式;
1、输入的事件;
2、Looper 添加消息;
输入事件:点击屏幕或按键按下,得到系统响应。
答:ANR是指在5s内没有响应输入事件(例如:按键按下、屏幕触摸),而输入的事件、Looper 添加消息都可以唤醒 Looper 里面的 block。Looper 死循环 与 ANR没有关系。