view.requestLayout源码解析

2019-10-23  本文已影响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才会被获取到去执行。

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

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卡顿过吗?

上一篇下一篇

猜你喜欢

热点阅读