美文网首页
view.requestLayout源码解析

view.requestLayout源码解析

作者: 求闲居士 | 来源:发表于2019-10-23 18:17 被阅读0次

    1. 根view

    view的requestLayout

    public void requestLayout() {
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
    
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
    }
    

    从activity的setContentView中可以看出,activity的根view是DecorView,setContentView的view也是add到id为ID_ANDROID_CONTENT的FrameLayout下。

    在ActivityThread中启动activity时,会调用handleResumeActivity方法,将DecorView 添加到ViewRootImpl中,DecorView的父类是ViewRootImpl。

    final Activity a = r.activity;
    View decor = r.window.getDecorView();
    ViewManager wm = a.getWindowManager();
    wm.addView(decor, l);
    

    WindowManagerImpl 是 wm 的实现类,wm.addView最终调用了WindowManagerGlobal 的addView

     public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
        ...
        root = new ViewRootImpl(view.getContext(), display);
        ...
        root.setView(view, wparams, panelParentView);//由它去设置view
    ...
    }
    

    在ViewRootImpl#setView () 会通过跨进程的方式向 WindowManagerService 发起一个调用,将 DecorView 最终添加到 Window 上。

    所以,调用父类的requestLayout,最终会调用到根view DecorView,而DecorView的父类是ViewRootImpl

    2. ViewRootImpl

    子view的requestLayout最终还是ViewRootImpl的requestLayout被调用了,现在来看requestLayout到底做了什么。

    @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                checkThread();  //检测线程
                mLayoutRequested = true;
                scheduleTraversals();
            }
        }
    

    检查当前线程是否为主线程,再调用scheduleTraversals

    void scheduleTraversals() {
        //1、注意这个标志位,多次调用 requestLayout,要这个标志位false才有效
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 2. 同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 3. 向 Choreographer 提交一个任务
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            //绘制前发一个通知
            notifyRendererOfFramePending();
            //这个是释放锁,先不管
             pokeDrawLockIfNeeded();
        }
    }
    
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            //遍历绘制的开端
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
    1. 这里先防止多次requestLayout的调用
    2. 再通过同步屏障停止同步消息,优先执行异步消息
    3. 向 Choreographer 提交一个任务,mTraversalRunnable则是要执行绘制的回调

    下面先介绍下同步屏障和mTraversalRunnable的执行时机。

    2.1 同步屏障

    在ViewRootImpl中,主要是通过获取Message设置setAsynchronous和调用MessageQueen的postSyncBarrier()来设置异步任务,在MessageQueue的next()方法中,如果设置了同步屏障,则跳过同步任务,只执行异步任务。

    //MessageQueue
    Message next() {
        for (;;) {
            synchronized (this) {
                Message prevMsg = null;
                Message msg = mMessages;
                //同步屏障,通过target和isAsynchronous来实现
                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());
                }
                
            }
        }
    }
    

    Looper的loop() 中,会调用MessageQueue的next()来遍历获取消息,而next()方法中,当msg.target == null且msg.isAsynchronous()为true的Message才会被获取到去执行。

    • msg.target == null这个条件是通过mHandler.getLooper().getQueue().postSyncBarrier()发送了个target为空的消息来实现同步屏障。
    • msg.isAsynchronous()为true,通过过获取Message设置setAsynchronous(true)

    二者缺一不可,少了其中一个就是无意义的了。 只有同步任务没有异步任务去设置同步屏障,或有异步任务但不设置同步屏障,都是没有意义的。

    2.2 Choreographer

    mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    
    /**
     * Posts a callback to run on the next frame.
     * <p>
     * The callback runs once then is automatically removed.
     * </p>
     *
     * @param callbackType The callback type.
     * @param action The callback action to run during the next frame.
     * @param token The callback token, or null if none.
     *
     * @see #removeCallbacks
     * @hide
     */
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }
    

    注释第一句话说的很清楚了,将回调放在下一帧调用。接下来简单看看具体如何实现下一帧调用回调。

     private void postCallbackDelayedInternal(int callbackType,
                Object action, Object token, long delayMillis) {
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            //添加任务到队列
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
    
            //上面的postCallback过来,延迟是0
            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);
            }
        }
    }
    

    将任务加入队列mCallbackQueues中,再执行scheduleFrameLocked方法

    private void scheduleFrameLocked(long now) {
        if (isRunningOnLooperThreadLocked()) {
            scheduleVsyncLocked();
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtFrontOfQueue(msg);
        }
    }
    
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }
    

    。如果运行在Looper线程上,则立即调度vsync;否则,通过Handler发一个异步消息到消息队列,最终也是到主线程处理,这个消息时异步消息,在先前同步屏障的作用下,会优先执行

    mDisplayEventReceiver = USE_VSYNC
        ? new FrameDisplayEventReceiver(looper, vsyncSource)
        : null;
    

    FrameDisplayEventReceiver是继承DisplayEventReceiver

    /**
     * Schedules a single vertical sync pulse to be delivered when the next
     * display frame begins.
     */
    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            nativeScheduleVsync(mReceiverPtr);
        }
    }
    
    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
        onVsync(timestampNanos, builtInDisplayId, frame);
    }
    

    还是看注释,scheduleVsync方法是调用JNI方法nativeScheduleVsync请求vsync信号,在下个vsync信号来的时候调用回调方法dispatchVsync

    onVsync则是FrameDisplayEventReceiver中实现的

     @Override
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
    @Override
    public void run() {
        mHavePendingVsync = false;
        doFrame(mTimestampNanos, mFrame);
    }
    

    onVsync发送了个异步消息,消息传了个callback Runnable用于回调,回调中调用了doFrame方法。

    doFrame方法会调用doCallbacks,根据回调类型从mCallbackQueues取出CallbackRecord,如果回调类型是Choreographer.CALLBACK_TRAVERSAL,则执行回调任,也就是ViewRootImpl的mTraversalRunnable

    void doFrame(long frameTimeNanos, int frame) {
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
        
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
        
            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
        
            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
        
            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    void doCallbacks(int callbackType, long frameTimeNanos) {
        //1.根据获取任务
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                        now / TimeUtils.NANOS_PER_MS);
        //2.最后一个类型回调,提交这一帧的时间
         if (callbackType == Choreographer.CALLBACK_COMMIT) {
            final long jitterNanos = now - frameTimeNanos;
            Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
            if (jitterNanos >= 2 * mFrameIntervalNanos) {
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                        + mFrameIntervalNanos;
                if (DEBUG_JANK) {
                    Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                            + " ms which is more than twice the frame interval of "
                            + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                            + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                            + " ms in the past.");
                    mDebugPrintNextFrameTimeDelta = true;
                }
                frameTimeNanos = now - lastFrameOffset;
                mLastFrameTimeNanos = frameTimeNanos;
            }
        }
        //3.执行回调任务
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "RunCallback: type=" + callbackType
                        + ", action=" + c.action + ", token=" + c.token
                        + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
            }
            c.run(frameTimeNanos);
        }
    }
    
    image

    图中 doCallbacks 从 frameTimeNanos2 开始执行,执行到进入 CALLBACK_COMMIT 时,经过了2.2帧。

    mTraversalRunnable执行就是在draw这步。

    2.3 doTraversal

    又回到了ViewRootImpl中,上面介绍了TraversalRunnable这个方法实际调用的是doTraversal()。

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //移除同步屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
            //执行绘制
            performTraversals();
    
            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
    

    同步屏障是为了给Choreographer中的异步消息让步,当回调到这里时,可以关闭同步屏障,执行后续的方法performTraversals了。

    private void performTraversals() {
        //mView就是setContentView的view
        final View host = mView;
        
        // mAttachInfo 赋值给View
        host.dispatchAttachedToWindow(mAttachInfo, 0);
        
        if (!mStopped || mReportNextDraw) {
            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                    (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
            if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                    || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                    updatedConfiguration) {
                //开始第一次测量大小
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);    
                        
                 //如果有设置权重,比如LinearLayout设置了weight,需要测量两次
                if (measureAgain) {
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                }
            }
            
        }
        //布局
         performLayout(lp, mWidth, mHeight);
         
         if (triggerGlobalLayoutListener) {
            mAttachInfo.mRecomputeGlobalAttributes = false;
            //调用dispatchOnGlobalLayout
            mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
        }
        
        //绘制
        performDraw();
    }
    

    分别调用了performMeasure,performLayout和performDraw,这三个方法开始遍历子view进行测量,布局和绘制。

     private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            //开始测量
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    

    如何实现测量,可以看看Measure要知道的点

    总结

    View.requestLayout -> ViewRootImpl.requestLayout -> ViewRootImpl.scheduleTraversals -> Choreographer.postCallback -> DisplayEventReceiver.scheduleVsync() -> Choreographer.doFrame -> Choreographer.doCallbacks ->ViewRootImpl.doTraversal -> ViewRootImpl.performTraversals

    从子view的requestLayout,到ViewRootImpl的requestLayout,在给Choreographer添加任务,Choreographer通过DisplayEventReceiver来监听vsync信号,回调执行ViewRootImpl的doTraversal开始进行绘制。

    参考

    ViewRootImpl 和 DecorView 分析

    Handler,MessageQueue,Looper,你所不知道的Asynchronous

    面试官又来了:你的app卡顿过吗?

    相关文章

      网友评论

          本文标题:view.requestLayout源码解析

          本文链接:https://www.haomeiwen.com/subject/teokvctx.html