跟Handler有关系的,包括Thread,Looper,Handler,MessageQueue
Looper:
由于Looper是android包加入的类,而Thread是java包的类,所以,想要为Thread创建一个Looper,需要在线程内部调用Looper.prepare
Looper内部会存储一个ThreadLocal,因此每个线程都会有自己的一个Looper。
Looper内部有自己存储了一个MessageQueue,以及主线程的MainLooper。
调用Looper一般会有两个方法:Looper.prepare以及Looper.loop方法
Looper.prepare
prepare会新创建一个Looper,塞进ThreadLocal,因此prepare必须在线程内部调用,才能将线程本身作为key。
同时,会创建MessageQueue,以及存储当前线程。
顺便看下两个比较常用的方法:
myLooper是从ThreadLocal中获取的当前线程所属的Looper。
myQueue对应的是当前线程的Looper中存储的MessageQueue。
Looper.loop
从Looper.loop方法,可以看出几个细节:
- 通过调用MessageQueue.next获取下一个要处理的Message
- 通过Message.target.dispatchMessage,将Message提交给Message中存储的Handler去处理,Handler调用dispatchMessage
- 可以创建一个进程唯一的Observer去监听Message的分配以及处理结束的进度。
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;final MessageQueue queue = me.mQueue;for (;;) {Message msg = queue.next(); // might blockif (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 loggerfinal 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 (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
从Looper.loop方法可以看出,会对Looper的MessageQueue遍历,不断取出Message,然后调用Message.target.dispatchMessage方法。
从Message的变量可以看出,target实际是Message所属的Handler。
同时Message存储了一个next,说明MessageQueue是一个链式结构。
MessageQueue
https://www.cnblogs.com/jiy-for-you/p/11707356.html
从MessageQueue.next可以获取几个有效信息:
- nativePollOnce:MessageQueue中没有Message的时候会卡在这个方法,类似于object.wait,当有人调用MessageQueue.enqueueMessage方法的时候,会将线程唤醒。
- msg.target == null,代表该msg是一个同步屏障(即阻拦同步消息的执行)。遇到同步屏障时,会往后遍历,优先执行异步消息(触发view的绘制的那个消息就是异步消息)。
- 如果获取到一个msg,msg.when代表的执行时间还没到,会先去执行IdleHandler里面的消息(或MessageQueue队列为空)。
// MessageQueue.next
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 iterationint 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) {// 同步屏障,当遇到同步屏障,会往后寻找异步消息(isAsynchronous)执行// Stalled by a barrier. Find the next asynchronous message in the queue.do {// 遍历,直到找到一个异步的msgprevMsg = msg;// 这一步,prevMsg!=null,这会导致msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// msg.when是这个message的执行时间,如果message的执行时间在now之后// 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.// 走到这,说明消息队列是空的,或队首是一个延迟执行的Messageif (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 handlerboolean 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;}
}
Handler的总结:
- Handler内部一定会有一个Looper。Looper跟线程一一绑定。绑定的关系存在Looper的sThreadLocal中。所以如果Handler想要监听哪个线程上的消息,可以直接给Handler传那个线程的Looper即可。
- Looper实际上是一个轮询消息的机制,所以内部一定会存在一个MessageQueue。当Looper开始轮询的时候(调用Looper.loop),会每次调用MessageQueue.next取一个Message出来执行。
- Looper获取到Message之后,会调用Message.target.dispatchMessage方法。即实际调用的是Handler.dispatchMessage的方法。
- Message中有几个比较重要的参数:
- target:这个Message从属于哪个handler(从哪个handler post过去的)。
- callback:当调用handler.postRunnable,即创建了一个Message,msg.callBack = runnable。
- what:这个Message的唯一标识id。当Handler.handleMessage方法中,会接收多个message,通过what区分这个Message的类别。
- when:通过handler.postDelayed,设置这个Message实际应该执行的时间:curTime+delay。MessageQueue的入队实际是通过when去进行Message的排序的。
- handler.dispatchMessage方法,
- 如果Message.callback != null,直接执行Message.callback.run(即post(Runnable))中Runnable的执行。
- 否则如果给handler设置了Callback,就调用Callback.handleMessage
- 否则,调用Handler本身的handleMessage方法(空实现),需要重写。
关于barrier
// MessageQueue
private int postSyncBarrier(long when) {// Enqueue a new sync barrier token.// We don't need to wake the queue because the purpose of a barrier is to stall it.synchronized (this) {final int token = mNextBarrierToken++;final Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;// 代表Message的barrier,特征是target == nullMessage prev = null;Message p = mMessages;if (when != 0) {// 根据when,将代表barrier的msg插入MessageQueuewhile (p != null && p.when <= when) {prev = p;p = p.next;}}if (prev != null) { // invariant: p == prev.nextmsg.next = p;prev.next = msg;} else {msg.next = p;mMessages = msg;}return token;}
}
postSyncBarrier,生成一个target == null的Message,根据when,插入MessageQueue中。返回的token是barrier的唯一标识。只要postSyncBarrier,就要根据这个token,后面移除barrier。否则会导致同步消息一直无法执行。
看下有了barrier的MessageQueue取Message的时候是怎么表现的。
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 iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);// 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) {// 如果取msg的时候,队首的Msg是Barrier// Stalled by a barrier. Find the next asynchronous message in the queue.do {prevMsg = msg;msg = msg.next;// 就一直往后遍历,寻找一个异步的msg} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// 如果还没到msg的执行时间,就设置nextPollTimeoutMillis// 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 {// 如果mMessages队列为空,或有Barrier的时候,异步msg为空,就设置等待时间为-1// 为-1代表等待被唤醒// 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();}// 如果队列为空,或者还没到message的执行时间,开始执行IdleHandlerif (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 handlerboolean 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.// 防止下次再走一遍IdleHandlerpendingIdleHandlerCount = 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.// 经过IdleHandler之后,可能已经有Message入队了,再遍历一遍,第二次就直接等待了。nextPollTimeoutMillis = 0;}
}
总结下next的逻辑:
- 如果mMessages的队首是barrier(msg.target == null),就遍历messages,优先执行异步消息(异步消息一般是优先级最高的信息:比如响应input事件或是view刷新。)。
- 如果不是barrier,就直接取队首的message执行。
- 如果1,2步骤取到的message != null,先看message的when < now,大于则直接返回message给Looper.loop方法。小于,则设置nextPollTimeoutMillis,用来设置线程的等待时间:nativePollOnce。
- 如果messageQueue为空,或message.when > now(即要等待),那么这个时候,就去执行IdleHandler。
总结下上面,nativePollOnce其实代表,线程在等待下一个消息的执行,或者messages队列为空。或者是设置了barrier情况下,没有异步消息的时候。
下一步,看下MessageQueue的具体打出日志代表什么。