Offer to come, dig friends take it! I am participating in the 2022 Spring Recruit Punch card activity. Click here for details.

preface

We know that a View has a set of POST methods that emit some page processing in a non-UI thread. The View also has another postInvalidate method, which also draws from non-UI threads.

It’s also sending messages from non-UI threads to UI threads, but there’s a big difference.

1, postInvalidate

So let’s look at postInvalidate

public void postInvalidate(a) {
    postInvalidateDelayed(0);
}


public void postInvalidateDelayed(long delayMilliseconds) {
    // We try only with the AttachInfo because there's no point in invalidating
    // if we are not attached to our window
    final AttachInfo attachInfo = mAttachInfo;
    if(attachInfo ! =null) {
        attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); }}Copy the code

You can see that the process ends when mAttachInfo is null. MAttachInfo is null when the view is DetachedFromWindow:

void dispatchDetachedFromWindow(a) {
    AttachInfo info = mAttachInfo;
    if(info ! =null) {
        int vis = info.mWindowVisibility;
        if(vis ! = GONE) { onWindowVisibilityChanged(GONE); } } onDetachedFromWindow(); onDetachedFromWindowInternal(); InputMethodManager imm = InputMethodManager.peekInstance();if(imm ! =null) {
        imm.onViewDetachedFromWindow(this);
    }


    ListenerInfo li = mListenerInfo;
    finalCopyOnWriteArrayList<OnAttachStateChangeListener> listeners = li ! =null ? li.mOnAttachStateChangeListeners : null;
    if(listeners ! =null && listeners.size() > 0) {
        for (OnAttachStateChangeListener listener : listeners) {
            listener.onViewDetachedFromWindow(this); }}if((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) ! =0) {
        mAttachInfo.mScrollContainers.remove(this);
        mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;
    }


    mAttachInfo = null;
    if(mOverlay ! =null) { mOverlay.getOverlayView().dispatchDetachedFromWindow(); }}Copy the code

So postInvalidate is invalidated when the view is removed from the page.

When mAttachInfo is not null, then execute mViewRootImpl dispatchInvalidateDelayed function, the code is as follows:

public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
    Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
    mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
Copy the code

The message is sent directly with mHandler.

2, post

Now let’s look at post

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if(attachInfo ! =null) {
        return attachInfo.mHandler.post(action);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().post(action);
    return true;
}
Copy the code

Also, when mAttachInfo is not null, messages are sent directly using mHandler.

But! Pay attention to the buts! When mAttachInfo is null, instead of terminating the process directly, runnable is stored to a RunQueue. RunQueue is a queue with the following code:

static final class RunQueue {
    private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();


    void post(Runnable action) {
        postDelayed(action, 0);
    }


    void postDelayed(Runnable action, long delayMillis) {
        HandlerAction handlerAction = new HandlerAction();
        handlerAction.action = action;
        handlerAction.delay = delayMillis;


        synchronized(mActions) { mActions.add(handlerAction); }}void removeCallbacks(Runnable action) {... }void executeActions(Handler handler) {
        synchronized (mActions) {
            final ArrayList<HandlerAction> actions = mActions;
            final int count = actions.size();


            for (int i = 0; i < count; i++) {
                finalHandlerAction handlerAction = actions.get(i); handler.postDelayed(handlerAction.action, handlerAction.delay); } actions.clear(); }}private static class HandlerAction {... }}Copy the code

When the executeActions function of RunQueue is called, the queue is traversed and the handler is used to send messages.

So when is executeActions called?

In view otimpl’s performTraversals function, as follows:

private void performTraversals(a) {...// Execute enqueued actions on every traversal in case a detached view enqueued an actiongetRunQueue().executeActions(mAttachInfo.mHandler); . }Copy the code

PerformTraversals is called in the doTraversal function

void doTraversal(a) {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);


        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }


        performTraversals();


        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false; }}}Copy the code

DoTraversal is performed in mTraversalRunnable, a Runnable object in ViewRootImpl

final class TraversalRunnable implements Runnable {
    @Override
    public void run(a) { doTraversal(); }}final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
Copy the code

MTraversalRunnable is posted in the scheduleTraversals function of ViewRootImpl

void scheduleTraversals(a) {
    if(! mTraversalScheduled) { mTraversalScheduled =true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
Copy the code

ScheduleTraversals are performed in a number of places, such as:

void handleAppVisibility(boolean visible) {
    if(mAppVisible ! = visible) { mAppVisible = visible; scheduleTraversals();if(! mAppVisible) { WindowManagerGlobal.trimForeground(); }}}void handleGetNewSurface(a) {
    mNewSurfaceNeeded = true;
    mFullRedrawNeeded = true; scheduleTraversals(); }...Copy the code

Here is not a list, you can be interested in their own source code to search.

To summarize, when a view is removed from a page, messages sent via the POST series of functions are not immediately sent to the handler, but are placed in a queue. When the view is added to the page again, the message is fetched from the queue and sent by handler.

3, summarize

So when we use

post(new Runnable() {
    @Override
    public void run(a) { invalidate(); }});Copy the code

It’s different from postInvalidate.