Android Handler源码解析

364 阅读9分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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

1076f4f9a494be5dd807aa6be73984d.png

这里扩展一下,看到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()

7d0d22e8c25a1d087b622e01c488256.png

这里就是发同步屏障消息的地方,也是在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,人家会玩的都会用的,说最好不要用的,我觉得,可能我没达到大佬的那种境界,没理解那种想法吧

80e4964d2849a75faf8d77c16c98e53.png

继续分析源码啊,然后执行完这段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就是设置这个消息是异步消息。所以你能看出系统绘制的时候,会把屏障消息和异步消息一起使用,是为了能让异步消息先去执行。

所以我之前写错了,改正后结论就是,我们平时发送的消息是同步消息,系统绘制流程中会发送一条屏障消息之后,再发送一条异步消息。