Handler机制分析
Handler机制组成元素之间的关系
Handler机制主要有Handler、MessageQueue、Message、Looper几个元素构成。
它们之间的关系是:
一个线程只有一个Looper实例
Looper中持有队列mQueue
Handler持有队列mQueue和Looper对象。在构造Handler实例时如果没有Looper入参,那就默认使用当前线程的Looper,
ThreadLocal<Looper>.get()Message中持有handler和next message
MessageQueue中持有当前message
生产消息:Handler发送的信息通过MessageQueue.enqueueMessage将消息入队
消费消息:Looper.loopOnce中将MessageQueue的消息取出,调用Message.target.dispatchMessage,target属性就标记了消息最终交给哪个Handler处理,所以这里的含义是在生产Msg的Handler中执行处理逻辑;如果MessageQueue信息为空,就会执行被挂起的IdleHandler。
dispatchMessage方法分析
dispatchMessage方法关系到消息队列中消息所对应的处理逻辑最终在哪如何被处理
title:android.os.Handle.dispatchMessage
|
1 | /** * Handle system messages here. */ public void dispatchMessage( Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } |
|
方法有三个逻辑分支,都是处理MessageQueue抛出的Msg:
msg.callback不为空
message.callback.run()Message的callback成员是一个Runnable对象Handler.mCallback不为空
由Handler.Callback的接口实现来处理msg.callback和Handler.mCallback都为空
由Handler.handleMessage方法处理,子类没重写则默认不处理
Handler处理并发实现
title:android.os.MessageQueue.enqueueMessage消息入队
|
1 | boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } // 因为可能在任意对象操作入队,而只会在looper所绑定的线程出队,所以这里加对象锁,保证入队出队操作是线程安全的 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; 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; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } |
|
title:android.os.MessageQueue.next消息出队
|
1 | 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); // 用来检查消息队列中是否有新的消息要处理,当队列为空时,`nativePollOnce` 会使线程等待直到:1. 有新消息到达。2. 被唤醒去处理其他任务(例如,定时事件、输入事件等)。3. 明确使用 `wakeUp()` 方法唤醒。 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; } } |
|
title:android.os.Looper
|
1 | private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { 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; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } // 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); 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; } /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ @SuppressWarnings("AndroidFrameworkBinderIdentity") 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 (;;) { if (!loopOnce(me, ident, thresholdOverride)) { return; } } } |
|
Handler的并发处理其实就是消息入队和出队被处理的过程:
可以在任意线程将消息入队,具体线程由Handlder.sendMsg的方法栈决定
只会在Looper.loop方法中执行出队,而Looper.loop只会在指定的一个线程中执行的,也就是消息最终被处理的线程
可以看出入队和出队可能是在不同的线程中执行的,在MessageQueue中通过对象锁来保证线程安全
Hanlder与ANR的关系
消息阻塞机制
当主线程阻塞超过5s之后,就会触发ANR;前面我们知道,在Looper开启死循环取消息的时候,如果消息队列中没有消息的时候,就可能会被block,调用了nativePollOnce,那么为什么没有阻塞主线程呢?
其实应该把这分为两件事来看,looper.loop是用来处理消息,当没有消息的时候,主线程就休息了,不需要干任何事;像input事件,其实就是一个Message,当它加入到消息队列的时候,会调用nativeWake唤醒主线程,主线程来处理这个消息,只有处理这个消息超时,才会发生ANR,而不是死循环会导致ANR。
案例分析
title:”线程休眠”
|
1 | "main" prio=5 tid=1 Native | group="main" sCount=1 dsCount=0 flags=1 obj=0x7185b6a8 self=0xb400007375b4bbe0 | sysTid=3433 nice=0 cgrp=default sched=0/0 handle=0x749c9844f8 | state=S schedstat=( 800801640 66783841 881 ) utm=60 stm=19 core=0 HZ=100 | stack=0x7fc20cb000-0x7fc20cd000 stackSize=8192KB | held mutexes= native: #00 pc 000000000009ca68 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) native: #01 pc 0000000000019d88 /system/lib64/libutils.so (android::Looper::pollInner(int)+184) native: #02 pc 0000000000019c68 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112) native: #03 pc 0000000000112194 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) at android.os.MessageQueue.nativePollOnce(Native method) at android.os.MessageQueue.next(MessageQueue.java:335) at android.os.Looper.loop(Looper.java:183) at android.app.ActivityThread.main(ActivityThread.java:7723) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997) |
|
在我们分析ANR日志时,经常会看到这样表现,结合上面我们对于Handler的了解,这个时候其实就是没有消息了,我们看已经调用了nativePollOnce方法,此时主线程就休眠了,等待下一个消息到来。
title:”ANR堆栈”
|
1 | "main" prio=5 tid=1 Blocked | group="main" sCount=1 dsCount=0 flags=1 obj=0x7185b6a8 self=0xb400007375b4bbe0 | sysTid=3906 nice=-10 cgrp=default sched=0/0 handle=0x749c9844f8 | state=S schedstat=( 2591708189 61276010 2414 ) utm=220 stm=38 core=5 HZ=100 | stack=0x7fc20cb000-0x7fc20cd000 stackSize=8192KB | held mutexes= // ...... - waiting to lock <0x0167ghe6d> (a java.lang.Object) held by thread 5 // ...... 方法调用 at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7723) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997) |
|
在这段堆栈中,我们看到主线程已经是出问题了,处于Blocked的状态,那么在Handler调用dispatchMessage方法的时候,是调用了handleCallback,说明此时是调用了post方法,在post方法中,主线程一直想要获取其他线程持有的一把锁,导致了超时产生了ANR。