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();
- 这里先防止多次requestLayout的调用
- 再通过同步屏障停止同步消息,优先执行异步消息
- 向 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.
*
* The callback runs once then is automatically removed.
*
*
* @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);
}
}
图中 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卡顿过吗?