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(@NonNull Message msg) {  	if (msg.callback != null) {  		handleCallback(msg);  	} else {  		if (mCallback != null) {  			if (mCallback.handleMessage(msg)) {  				return;  			}  		}  		handleMessage(msg);  	}  }

|

方法有三个逻辑分支,都是处理MessageQueue抛出的Msg:

  1. msg.callback不为空
    message.callback.run() Message的callback成员是一个Runnable对象

  2. Handler.mCallback不为空
    由Handler.Callback的接口实现来处理

  3. 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的并发处理其实就是消息入队和出队被处理的过程

  1. 可以在任意线程将消息入队,具体线程由Handlder.sendMsg的方法栈决定

  2. 只会在Looper.loop方法中执行出队,而Looper.loop只会在指定的一个线程中执行的,也就是消息最终被处理的线程

  3. 可以看出入队和出队可能是在不同的线程中执行的,在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。