View.post()源码解析

2020-09-07  本文已影响0人  code希必地

很多情况下,我们在Activity的onCreate()中调用View.post()去解决View宽高为0的问题,为什么可以这么做呢?下面我们就带着这个问题去看下源码。

源码分析

1、View.post()

先看下View.post()中到底做了什么?

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

代码很简单,如果attachInfo不为空,则会执行attachInfo.mHandler.post(action);,否则执行getRunQueue().post(action);。这里先看下它们分别做了什么。

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

    public void postDelayed(Runnable action, long delayMillis) {
        final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

        synchronized (this) {
            if (mActions == null) {
                mActions = new HandlerAction[4];
            }
            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
            mCount++;
        }
    }

从代码中可以看出,其实就是将Runnable和delayMillis封装到HandlerAction中,并缓存了起来。
这里对View.post()做一个简单的总结:
如果mAttachInfo不会空则直接调用attachInfo.mHandler的post方法,否则将Runnbale和delayMillis封装到HandlerAction中并缓存起来(何时执行缓存的Runnable,后面会讲到)。那么问题来了,mAttachInfo是什么时候赋值的呢?

2、mAttachInfo是什么时候赋值的呢?

经搜索发现是在dispatchAttachedToWindow(AttachInfo info, int visibility)中将info赋值给View的成员变量mAttachInfo,在dispatchDetachedFromWindow()将其置成null。

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
       //省略代码...
        // Transfer all pending runnables.
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        onAttachedToWindow();
    //省略代码...
    }

dispatchAttachedToWindow()方法中除了给mAttachInfo进行赋值,还会调用mRunQueue.executeActions(info.mHandler);

 public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }

这个方法就是取出缓存的HandlerAction,然后调用Handler的postDelayed()去执行缓存的Runnbale。还记得我们在View.post()中当mAttachInfo==null时会将Runnable给缓存起来,就是在这里调用的啦。
那么View的dispatchAttachedToWindow()又是在哪里调用的呢?

3、View的dispatchAttachedToWindow()的调用

熟悉ViewRootmpl的同学肯定都知道,它是在performTraversals()中调用的,这里不做过多的分析,只简单说下流程。

搜狗截图20200907132122.png
我们主要看下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();
        }
    }

mChoreographer.postCallback()最终会调用postCallbackDelayedInternal()

private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

这里只说结论,就不具体分析了:

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

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

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

            performTraversals();

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

执行mTraversalRunnable则会执行doTraversal(),在doTraversal()中又会执行performTraversals()

private void performTraversals() {
        // mView就是DecorView,在setView()时进行的赋值
        final View host = mView;

        //省略部分代码....
        
        //默认为true
        if (mFirst) {
            mFullRedrawNeeded = true;
            mLayoutRequested = true;
            //省略部分代码....
            
            //调用DecorView的dispatchAttachedToWindow(),DecorView是FrameLayout,则会调用ViewGroup的
            //dispatchAttachedToWindow(),ViewGroup的dispatchAttachedToWindow()会遍历child调用child的dispatchAttachedToWindow()
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
            dispatchApplyInsets(host);
        } else {
            //省略部分代码....
        }
        if(....){
            performMeasure();
        }
        if(....){
            performLayout();
        }
        if(....){
            performDraw();
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读