持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
前言
短期内没有什么比较好的知识点分享,因为我现在接触的都是一些比较深的东西,我自己也是一知半解,像这种情况就不太敢把这些比较深的技术写出来误人子弟。所以暂时决定开一个源码分析的专题吧,这样以后如果用到的话看自己的文章也能更快的回想起来。但我觉得写Handler应该不会有多少人看,大部分人只要开发过两三年的肯定都知道它内部的一个实现。
Android里面其实比较重要的就是Handler机制和Binder机制,一个用于进程内的通信,一个用于进程间的通信,比如我们写一些AIDL如果不是跨进程的话,或者局部广播,其实内部用的都是Handler。
基础概念
解析前还是可以说一些基础概念,针对没接触过的人,如果对Handler有一定理解,可以直接跳过。
Handler机制主要的角色有Handler、Looper和MessageQueue。
然后这个Looper是个死循环,为什么要设计成这样呢?主要原因就是让应用一直执行,其实不光是Android这边,也有其它的领域也会用到loop这种操作,也就是死循环,来保证进程的一直执行。
Handler机制的原理是什么?它具体是怎么实现阻塞唤醒的?其实最终的实现是Linux的IO多路复用。如果刚接触的朋友,就先知道是这么一回事就行了,如果你不熟悉Linux,你去看什么事IO多路复用,肯能会不太能接受它的概念。
源码分析
我这里直接拿33的来分析,我也没看过33的源码,这边也是一边分析一边写,如果有说错的地方或者哪位大佬有一些其它的看法可以说出来。
从哪里开始讲好呢。从Looper开始吧。再啰嗦一句,我尽量把细节讲清楚,但是如果看不懂的话可以留言,我也会尽量解答,主要是你要先知道Handler机制的一个大概,才会能够接受我写的东西,如果是真的没接触过,那还是建议先了解下它的一个概念。
Looper
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
看到一个sThreadLocal的set方法,这个sThreadLocal就是一个
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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);
}
能看到拿到当前现线程,然后调getMap得到ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
从这里你就能看出,ThreadLocalMap是线程独有的,这样说能明白不?每个线程有自己独立的ThreadLocalMap。可以来看看ThreadLocalMap的数据结构
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
看到这里,ThreadLocalMap数据结构是什么?是数组吧?一个弱引用数组,特别还要注意一下,虽然ThreadLocalMap是ThreadLocal的静态内部类,但是它的实例是线程持有的
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
所以怎么解释上面的代码?我简单概括一下就是,线程里面弱引用的Looper数组,Looper的prepare方法就是创建一个Looper对象,然后把这个Looper对象放到线程的这个数组里面。(所以线程和Looper就关联了)
创建Looper对象,这就是下一步的入口
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
看到内部是创建了一个MessageQueue,关于MessageQueue后面再讲。
然后我们看它的死循环loop()方法
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;
}
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
拿到Looper对象,然后me.mInLoop就是判断有没有执行过loop(),执行过就报错嘛。
然后死循环调用loopOnce
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;
}
这里应该算是比较复杂的,认真看。
从Looper对象的MessageQueue消息队列中拿出消息。没有消息表示消息队列正在退出,注意这个没有消息不是继续循环再拿一直到拿到为止,是没有消息表示消息队列正在退出,这时候是会外面退出循环的。
核心就是me.mQueue.next()这个方法,里面才是真正的拿消息的逻辑,而且这个方法没有消息的情况下会阻塞。一定要和下面那行的判空区分,下面的判空是退出循环,正常的没有消息的流程是阻塞。
然后下面那堆代码,Trace相关的那堆,是和分析相关的,和主线没关系。Observer observer = sObserver这个是一个观察者的操作,可以设置,你这里当成是为null,不走判断的逻辑就行。
public static void setObserver(@Nullable Observer observer) {
sObserver = observer;
}
msg.target.dispatchMessage(msg);就是把消给Handler,msg.target就是Handler。然后后面那堆代码也是分析打印,直到msg.recycleUnchecked();是回收Message,这个我在后面讲Message的时候会说。
来个小结,简单的说,Looper的loop方法就是从MessageQueue中拿消息,然后把消息给Handler,最后回收Message
Handler
简单来说整个流程是Handler发消息给MessageQueue,然后Looper不断从MessageQueue拿消息。我这里觉得还是先讲插入消息,再讲拿消息比较好,所以先讲Handler再讲MessageQueue。
当Looper的机制运行起来之后,Handler发消息。
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
看到这里,其实发消息主要做的操作就是拿到MessageQueue,然后调用它的enqueueMessage方法。那MessageQueue是在哪里赋值的呢?
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到在构造方法中,也就是Handler在创建时,从当前的线程拿出Looper,拿出Looper中的MessageQueue
这里扩展一下,看到Handler默认传的async是false,这是什么意思呢?我后面会细讲。你这里先知道消息分为同步消息和异步消息,我们平时用Handler的post这些传的消息都是同步消息。
MessageQueue
我们暂时先看MessageQueue,后面再回去看Looper调用Handler的dispatchMessage方法。
MessageQueue其实细节挺多的,包括正常划分的话,消息队列还包含了native层还有个MessageQueue,这里我们分析的是java层的MessageQueue所做的操作。
我们接着Handler发消息那里看到enqueueMessage方法
boolean enqueueMessage(Message msg, long when) {
// handler为空就直接报错
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
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;
}
判断mQuitting是判断消息队列是否已停止,不用再执行后续的流程了。看到是在quit方法才会把这个值设为true,正常是不会停止的,所以这里也不帖quit的代码了。如果你在发消息的过程中,你的线程莫得了,才会走到这步。
Message p = mMessages就是拿消息队列,其实所谓的消息队列的数据结构,就是一个链表,所以这个就是链表的头结点。
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
如果消息链表里面没消息或者当前的消息when,就是延时的时间,小于链表头节点的消息的延时时间,则把这个消息放到链表头结点。needWake你就暂时理解成是否需要唤醒,如果往整个设计来说,它是和IO多路复用相关的,所以只把它当成是否需要唤醒,会比较容易理解。
else {
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;
}
如果当前消息不能插入到头结点中,则插入到链表指定位置,也就是链表是按when延时值来排序的。这里有个needWake && p.isAsynchronous() 则不需要唤醒。这个isAsynchronous()就是判断是不是异步消息,是异步消息就不用唤醒,这个我后面会一起简单说。
if (needWake) {
nativeWake(mPtr);
}
最后如果需要唤醒的话就调nativeWake,这个里面就是IO多路复用了,你可以先简单理解成调用这个方法之后,Looper那里的阻塞(下面讲为什么会阻塞)就会唤醒,然后开始走loop去拿消息。
做个小结吧,MessageQueue的enqueueMessage方法主要是把消息按when插入到Message链表中,然后判断如果不是同步消息就唤醒。(我上面也有说,我们Handler发的基本都是异步消息)
有存就有拿,还记得我们在Looper里面的loopOnce方法调用me.mQueue.next()吗,那里就是调用MessageQueue的newt方法拿数据。
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;
}
}
别看它代码有点多,其实每一处都还真有些重要的细节,所以这个方法很重要,如果没看懂的话,建议多看几遍。
可以看到一开始直接在循环里面nativePollOnce,这个可以先不用管,一开始是不会阻塞的。
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());
}
一开始很多人看不懂这里,msg.target == null 其实是异步消息,同步消息也就是我们用handler传的消息,targer是不会为空的。这里就是拿出消息队列中的第一条同步消息。意思就是这里的代码,接下来的流程,我们都是处理同步消息,不处理异步消息。
上面我也有多多少少提到一些异步消息同步消息的地方,这里可以简单介绍一下,其实Handler的消息队列分为同步消息和异步消息,同步消息就是我们平时使用Handler发送的消息。
这个异步消息是和Android绘制流程那里有关的,因为源码比较多,我就简单讲一下,如果看不懂的话,你就理解成异步消息是刷新页面的,是系统发送的。看不懂确实没必要太纠结我下面这段流程,可以直接往下继续看源码分析。
就是打开Activity页面的话,会走AMS那堆流程,然后执行到ActivityThread的handleResumeActivity方法,里面有WindowManager的addView方法,WindowManager是一个抽象,它的具体实现是WindowManagerGlobal,它的addView方法里面是调用了ViewRoot的setView,然后ViewRoot的实现,也就是ViewRootImpl经过一系列方法会调用到scheduleTraversals()
这里就是发同步屏障消息的地方,也是在ViewRootImpl里面去消费同步屏障消息,这里就不展开讲了,因为我主要是讲Handler的源码,这里和主线关系不大,只要先有这么一个概念就行。
好的,继续回到MessageQueue的next源码分析。
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;
}
判断是否有消息,没有消息则nextPollTimeoutMillis = -1;这个-1的意思是阻塞只能手动唤醒,不是延时唤醒。now < msg.when就是没到这条消息延时触发的时间,nextPollTimeoutMillis阻塞时间就设置成延时时间。如果到时间了,就返回msg,这个没什么好说的,这里返回msg,Looper那边就拿到Message去走Looper那边的流程。
所以我们假设没到延时时间,继续看下面做了什么操作。
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);
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);
}
}
}
这两段代码就是执行IdleHandler的流程,所以你至少得先知道IdleHandler是什么玩意,怎么去使用。
IdleHandler简单来说就是闲时触发的Handler,它的原理就在这里,因为当前时间没有达到任何一个Message的延时,所以这时候就是所谓的闲时,所以这里会执行IdleHandler的操作。
这里多说一句,网上有一个说法,不要使用IdleHandler,因为不可控。我的观点我不敢保证是对的,但是我的理解是,IdelHandler其实一般不是单独使用的,比如GC,也是用了IdleHandler,但它不会只用IdleHandler,内存不够的时候也会触发。我不知道怎么解释好,但是用GC来举这个例子,我想说明的是IdleHandler不是单独使用。你还是不理解这个意思我也没办法,这确实是只可意会。
比如ActivityThread,人家会玩的都会用的,说最好不要用的,我觉得,可能我没达到大佬的那种境界,没理解那种想法吧
继续分析源码啊,然后执行完这段IdleHandler的判断之后,因为是死循环,又会执行到nativePollOnce(ptr, nextPollTimeoutMillis);此时我们的nextPollTimeoutMillis是有延时时间的,nativePollOnce内部就是IO多路复用,这里不深入讲,你只用先理解这里会阻塞就行,nextPollTimeoutMillis>0的话是延时多久之后主动唤醒,也就是阻塞nextPollTimeoutMillis毫秒之后自动唤醒,继续走循环,如果是-1的话,就需要enqueueMessage的nativeWake去主动唤醒,当然nativeWake不仅仅是在enqueueMessage中有调用,准确来说是需需要nativeWake去主动唤醒。
到这里MessageQueue主线流程就讲完了,我们做个小结,next方法主要是拿第一条异步消息,判断延时时间是否已达到,已达到则返回,未达到则判断是否有IdleHandler,有的话执行IdleHandler,如果没有的话或者执行完的话,nativePollOnce进行阻塞。
回到Handler的dispatchMessage
前面有说Looper调用MessageQueue的next拿到Message之后会调用Handler的dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我觉得这里没什么好说的,就是判断有没有callback这个点,唯一有意思的是这个callback是一个hook的点,扩展一下知识吧。
然后流程就会执行到如果你是用post Runnable,这里最终就会执行到Runnable的run方法,这里没什么难点,就不过多解释了。
总结
主线流程应该都讲完了,没有漏吧,因为比较多,而且各种穿插,所以如果有漏的话,欢迎评论,但我感觉这篇确实写得挺长的了,感觉应该很少有人会能看完。
总之还是意思意思的总结下一些关键的技术点吧:
(1) Looper、Handler、MessageQueue
(2) Looper和线程和ThreadLocal的关系
(3) 同步消息和异步消息
(4) IdleHandler
(5) 这些过程有些地方是用同步代码块扩起来的,线程安全放心使用
(6) 写作不易,看完记得点赞
就这样!
后续修正
之前写文章的时候把同步消息和异步消息写反了, 我们平常用handler传的消息是同步消息不是异步消息。
PS:这里接下去的分析都是基于Android 29的源码,上面的是33的
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());
}
这段代码的意思是判断到这个消息是屏障消息时,会找到链表中的第一个异步消息进行处理。其实这是一个优先处理异步消息的逻辑。
看看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 prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
看到它没设置target,所以可以通过target为空来判断是屏障消息
在ViewRootImpl中的scheduleTraversals方法中(暂时就不解释这个方法了,简单的理解成绘制有关的流程一部分就行)
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
看到有用postSyncBarrier去插入一条屏障消息,然后看Choreographer的postCallback方法
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
......
synchronized (mLock) {
......
if (dueTime <= now) {
......
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
能看到setAsynchronous就是设置这个消息是异步消息。所以你能看出系统绘制的时候,会把屏障消息和异步消息一起使用,是为了能让异步消息先去执行。
所以我之前写错了,改正后结论就是,我们平时发送的消息是同步消息,系统绘制流程中会发送一条屏障消息之后,再发送一条异步消息。