如果视图树复杂,会使整个Traversal过程变长。因此,我们在开发过程中要控制视图树的复杂程度。减少不必要的层级嵌套。比如使用RelativeLayout可以减少复杂布局的嵌套。
如果频繁的触发requestLayout(),就可能会导致在一帧的周期内,频繁的发生布局计算,这也会导致整个Traversal过程变长。有的ViewGroup类型的控件,比如RelativeLayout,在一帧的周期内会通过两次layout()操作来计算确认子View的位置,这种少量的操作并不会引起能够被注意到的性能问题。但是如果在一帧的周期内频繁的发生layout()计算,就会导致严重的性能,每次计算都是要消耗时间的!而requestLayout()操作,会向ViewRootImpl中一个名为mLayoutRequesters的List集合里添加需要重新Layout的View,这些View将在下一帧中全部重新layout()一遍。通常在一个控件加载之后,如果没什么变化的话,它不会在每次的刷新中都重新layout()一次,因为这是一个费时的计算过程。所以,如果每一帧都有许多View需要进行layout()操作,可想而知你的界面将会卡到爆!卡到爆!需要注意,setLayoutParams()最终也会调用requestLayout(),所以也不能烂用!同学们在写代码的过程中一定要谨慎注意那些可能引起requestLayout()的地方啊!
UI控件的requestLayout 和invalidate 的调用都会调用到ViewRootImpl 中的scheduleTraversals();
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//同步屏障:往消息队列插入一个同步屏障消息,这时候消息队列中的同步消息不会被处理,而是优先处理异步消息。
//消息队列中do while循环,遍历链表,直到找到异步消息msg.isAsynchronous()才跳出循环交给Handler去处理这个异步消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(// 一 ,将线程放到队列,等待,最后的doFrame中执行TraversalRunnable
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending(); // 二
pokeDrawLockIfNeeded(); // 三
}
}
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;
//将一个 Runnable 或者一个 FrameCallback 添加到有序任务队列里,这里添加的action 就是mTraversalRunnable
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
//通过JNI,跟底层说,下一个vsync脉冲信号来的时候请通知我。然后在下一个vsync信号来的时候,就会收到底层的JNI回调,也就是dispatchVsync这个方法会被调用,然后会调用onVsync
scheduleFrameLocked(now);//请求下一个 Vsync 信号
} else {//超时 发消息请求
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);//doScheduleCallback
msg.arg1 = callbackType;
msg.setAsynchronous(true);//异步消息 优先级最高
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
doScheduleCallback -》 scheduleFrameLocked
private void scheduleFrameLocked(long now) {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);// 等时间到了再调用doframe ,和信号同步
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
mDebugPrintNextFrameTimeDelta = false;
Log.d(TAG, "Frame time delta: "
+ ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
}
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();//计算跳帧数
// 1 当前时间戳减去vsync来的时间,也就是主线程的耗时时间
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
//1帧是16毫秒,计算当前跳过了多少帧,比如超时162毫秒,那么就是跳过了10帧
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
//一个是主线程有其它耗时操作,导致doFrame没有机会在vsync信号发出之后16毫秒内调用
//还有一个就是当前doFrame方法耗时,绘制太久,下一个vsync信号来的时候这一帧还没画完,造成掉帧
//如何监控卡顿: 插入空消息到消息队列 比较Looper两次处理消息的时间差,比如大于3秒,就认为卡顿了
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
// 取余,计算离上一帧多久了,一帧是16毫秒,所以lastFrameOffset 在0-15毫秒之间,这里单位是纳秒
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}// 出现掉帧,把时间修正一下,对比的是上一帧时间
frameTimeNanos = startNanos - lastFrameOffset;
}
//2、时间倒退了,可能是由于改了系统时间,此时就重新申请vsync信号(一般不会走这里)
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}这里申请下一次vsync信号
scheduleVsyncLocked();
return;
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
//3 能绘制的话,就走到下面
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
//依次执行队列中的线程,执行完 队列回收
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 {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
doCallbacks中:
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);//实际执行action 中的run, 当 下 次 VSNC 中 断 到 来 时 执 行 任 务 队 列 里 的 所 有 任 务。
}
//每次 doFrame 都会调到这里来,由VSYNC 驱动而来
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();//4.5 执行遍历
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
if (mFirst) {//在activity首次进行对view树遍历绘制时,将自己的mAttachInfo 传达到子VIEW 中去
mFullRedrawNeeded = true;
mLayoutRequested = true;
// We used to use the following condition to choose 32 bits drawing caches:
// PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfiguration.setTo(host.getResources().getConfiguration());
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
// Set the layout direction if it has not been set before (inherit is the default)
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
}//在activity首次进行对view树遍历绘制时,将自己的mAttachInfo 传达到子VIEW 中去 4.6
host.dispatchAttachedToWindow(mAttachInfo, 0);
//通知每一个子视图它的宿主窗口的可见发生变化了
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);//执行监听
dispatchApplyInsets(host);
。。。。省略
//RunQueue.executeActions()这个接口在整个ViewRootImpl里只有一个地方调用,被执行的Runnable事实上是被主线程的Handler的post到它的执行队列里面了
//而我们post的Runnable要等待TraversalRunnable执行完才会去执行,而TraversalRunnable这里面又会进行measure,layout和draw流程,
//所以等到执行我们的Runnable时,此时的View就已经被measure过了,所以获取到的宽高就是measure过后的宽高。
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
}
//请求 WindowManager 计算 Window 的大小并创建绘画用的 Surface,
//因为所有的 View 都是要显示到 Window 里的,只有 Window 的大小确定了才能确定每
//个 View 的大小 4.8 执行重新layout
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
+ " overscan=" + mPendingOverscanInsets.toShortString()
+ " content=" + mPendingContentInsets.toShortString()
+ " visible=" + mPendingVisibleInsets.toShortString()
+ " visible=" + mPendingStableInsets.toShortString()
+ " outsets=" + mPendingOutsets.toShortString()
+ " surface=" + mSurface);
//一个应用程序窗口的绘图表面在创建完成之后,函数就会将得到的一个Surface对象保存在当前正在处理的WindowState对象的成员变量mSurface中
if (mPendingConfiguration.seq != 0) {
if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
+ mPendingConfiguration);
updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
mPendingConfiguration.seq = 0;
updatedConfiguration = true;
}
//省略
if (didLayout) {
performLayout(lp, mWidth, mHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
//如果当前正在处理的窗口的顶层视图内嵌有SurfaceView,那么用来描述它的一个DecorView对象的成员变量mPrivateFlags的值的View.REQUEST_TRANSPARENT_REGIONS位就会等于1
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);//计算顶层视图的位置和大小,即计算顶层视图所占据的区域
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
//将顶层视图所占据的区域作为窗口的初始化透明区域
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
//检查ViewRoot类的成员变量mTransparentRegion和mPreviousTransparentRegion所描述的区域是否相等。如果不相等的话,那么就说明窗口的透明区域发生了变化
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager 重新设置透明区域
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
//执行画图
performDraw();