View绘制机制
心是被委屈撑大的,优秀是用痛苦磨成的。— 《等一个人读书》
写在前面
作为Android开发人员,在应用开发过程中,都会遇到应用卡顿的情况,通过肉眼可以分辨,我相信大多数的开发者都能说出其卡顿的原因,无非就是主线程有耗时操作,当然不排除手机的性能很Low。
那么为什么卡顿时肉眼可以分辨呢?这就要从屏幕绘制机制说起了,卡顿是由于主线程有耗时操作,导致View绘制掉帧,屏幕每16毫秒绘制一次,每秒绘制60次,而肉眼可见的频率是每秒绘制少于24次,也就是说每秒绘制24次以上,肉眼是看不出来的,即使主线程有耗时操作导致View绘制掉帧。
走进源码
下面通过源码分析View绘制机制,耐心看完,收获颇多。
1.请求绘制
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
......
/**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
* tree. This should not be called while the view hierarchy is currently in a layout
* pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the
* end of the current layout pass (and then layout will run again) or after the current
* frame is drawn and the next layout occurs.
*
* <p>Subclasses which override this method should call the superclass method to
* handle possible request-during-layout errors correctly.</p>
*/
@CallSuper
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout(); // 1
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
......
}
众所周知,想要重绘View需要调用requestLayout()函数。
注释1:因为最顶层布局是DecorView,mParent是ViewParent接口的具体实现类ViewRootImpl,很明显调用到了ViewRootImpl的requestLayout()函数,如有不懂可以看《View绘制流程》。
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread(); // 1
mLayoutRequested = true;
scheduleTraversals(); // 2
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
void scheduleTraversals() {
// mTraversalScheduled防止多次调用requestLayout()函数触发绘制请求,直到上一帧绘制完成。
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 同步屏障,这个东西有点意思
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 3
// 向Choreographer提交绘制任务
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
// 发送一条绘制通知
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
}
注释1:checkThread()函数检查当前线程是否是主线程,如果不是主线程直接抛出异常。
注释2:scheduleTraversals()函数处理请求绘制任务。
注释3:Handler很有意思的一个知识点,往消息队列中插入一个同步屏障消息,这时候消息队列中的同步消息不会被处理,而是优先处理异步消息。比如启动Activity,由于UI的优先级最高,此时需要暂停其他任务,优先启动Activity。
public final class MessageQueue {
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) { // 1
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous()); // 2
}
......
}
......
}
}
}
注释1:msg不为空且target为空,进入消息队列寻找同步屏障消息。
注释2:直到msg.isAsynchronous()为true,跳出循环,处理同步屏障消息。
2.请求信号
public final class Choreographer {
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
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); // 1
// 2
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);
}
}
}
}
在ViewRootImpl的requestLayout()函数中调用了Choreographer的postCallback()函数提交绘制任务;postCallback()函数直接调用postCallbackDelayed()函数;postCallbackDelayed()函数检查参数,若参数合法则调用postCallbackDelayedInternal()函数,重点在postCallbackDelayedInternal()函数。
注释1:往队列中添加一个任务。
注释2:如果延时等于0,则直接调用 scheduleFrameLocked(now)函数;否则通过mHandler发送一条延时消息,最后依然会调用 scheduleFrameLocked(now)函数。延时很少会用到,我们只看没有延时的Case。
public final class Choreographer {
// Enable/disable vsync for animations and drawing.
private static final boolean USE_VSYNC = SystemProperties.getBoolean(
"debug.choreographer.vsync", true);
private void scheduleFrameLocked(long now) {
// 1
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
// 2
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
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);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
}
USE_VSYNC是一个系统标志,这里只分析USE_VSYNC等于true的情况。
注释1:通过mFrameScheduled标志防止多次调用,和ViewRootImpl的scheduleTraversals()函数中的mTraversalScheduled标志作用相同。
注释2:检查线程,如果当前线程是主线程直接调用scheduleVsyncLocked()函数,否则会通过mHandler发送一条同步屏障消息切换到主线程。
public final class Choreographer {
// The display event receiver can only be accessed by the looper thread to which
// it is attached. We take care to ensure that we post message to the looper
// if appropriate when interacting with the display event receiver.
private final FrameDisplayEventReceiver mDisplayEventReceiver;
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
// 省略,之后会讲
......
}
}
scheduleVsyncLocked()函数只有一行代码,mDisplayEventReceiver.scheduleVsync()又走到哪里呢?从上面的代码可以看出FrameDisplayEventReceiver继承自DisplayEventReceiver,DisplayEventReceiver是一个抽象类,实则走到了DisplayEventReceiver的scheduleVsync(),这里讲的有点抽象,Follow me 往下看。
public abstract class DisplayEventReceiver {
/**
* Called when a vertical sync pulse is received.
* The recipient should render a frame and then call {@link #scheduleVsync}
* to schedule the next vertical sync pulse.
*
* @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
* timebase.
* @param builtInDisplayId The surface flinger built-in display id such as
* {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}.
* @param frame The frame number. Increases by one for each vertical sync interval.
*/
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
}
/**
* 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 {
// 请求Vsync
nativeScheduleVsync(mReceiverPtr); // 1
}
}
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame); // 2
}
}
如代码所示,DisplayEventReceiver的确是一个抽象类,也的确存在一个scheduleVsync()函数。onVsync()函数是空实现,需要子类重写。
注释1:nativeScheduleVsync(mReceiverPtr)函数通过JNI向底层请求脉冲信号,底层每隔16毫秒就会发送一个信号,这个信号不会平白无故通知给View的。
注释2:View向底层请求脉冲信号,当下一个脉冲信号来的时候底层通过JNI回调dispatchVsync()函数,然后调用onVsync()函数通知View有信号来了,开始绘制吧。
public final class Choreographer {
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
// Ignore vsync from secondary display.
// This can be problematic because the call to scheduleVsync() is a one-shot.
// We need to ensure that we will still receive the vsync from the primary
// display which is the one we really care about. Ideally we should schedule
// vsync for a particular display.
// At this time Surface Flinger won't send us vsyncs for secondary displays
// but that could change in the future so let's log a message to help us remember
// that we need to fix this.
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
Log.d(TAG, "Received vsync from secondary display, but we don't support "
+ "this case yet. Choreographer needs a way to explicitly request "
+ "vsync for a specific display to ensure it doesn't lose track "
+ "of its scheduled vsync.");
scheduleVsync();
return;
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
long now = System.nanoTime();
// 1
if (timestampNanos > now) {
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ " ms in the future! Check that graphics HAL is generating vsync "
+ "timestamps using the correct timebase.");
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
// 2
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;
// 3
doFrame(mTimestampNanos, mFrame);
}
}
}
之前说过FrameDisplayEventReceiver继承自DisplayEventReceiver,FrameDisplayEventReceiver也重写了onVsync()函数。
注释1:更新时间戳,单位:纳米。
注释2:通过mHandler发送一条同步屏障消息,Message.obtain(mHandler, this)中callback传入this,当这条同步屏障消息得到处理时,FrameDisplayEventReceiver的run()函数就会被调用。
注释3:执行绘制任务。
public final class Choreographer {
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
// 如果上次绘制没有完成,直接return
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();
// 计算主线程的耗时时间,使用当前时间减去Vsync信号来的时间,就是主线程的耗时时间
final long jitterNanos = startNanos - frameTimeNanos;
// 每帧16毫秒,mFrameIntervalNanos默认等于16毫秒,
// 如果这个时间大于16毫秒,说明掉帧了。
if (jitterNanos >= mFrameIntervalNanos) {
// 计算跳过的帧数,每帧16毫秒,比如jitterNanos等于163毫秒,就是跳过了十帧
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
// SKIPPED_FRAME_WARNING_LIMIT默认等于30,
// 如果跳过了三十帧,系统就打出Log提示主线程是否存在耗时工作。
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
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;
}
// 可能用户修改了系统时间,出现时间倒退,则重新请求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.");
}
scheduleVsyncLocked();
return;
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
// 可以绘制
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);
}
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.");
}
}
}
doFrame()函数也存在耗时,可能会导致掉帧,接下来看doCallbacks()函数。
public final class Choreographer {
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
// We use "now" to determine when callbacks become due because it's possible
// for earlier processing phases in a frame to post callbacks that should run
// in a following phase, such as an input event that causes an animation to start.
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS); // 1
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
// Update the frame time if necessary when committing the frame.
// We only update the frame time if we are more than 2 frames late reaching
// the commit phase. This ensures that the frame time which is observed by the
// callbacks will always increase from one frame to the next and never repeat.
// We never want the next frame's starting frame time to end up being less than
// or equal to the previous frame's commit frame time. Keep in mind that the
// next frame has most likely already been scheduled by now so we play it
// safe by ensuring the commit time is always at least one frame behind.
// 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;
}
}
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
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));
}
// 3
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
注释1:从队列取出任务。
注释2:流程的最后一步,更新这一帧的时间,保证提交时间在最后一次vsync信号时间之后。
注释3:还记得ViewRootImpl的scheduleTraversals()函数中的 mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)提交绘制任务吗?c.run(frameTimeNanos)的c就是mTraversalRunnable。这就很神奇了不是,又回到了ViewRootImpl。
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); // 1
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals(); // 2
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
}
现在回到ViewRootImpl,找到TraversalRunnable,从代码可以看到TraversalRunnable的run()函数调用了doTraversal()函数。
注释1:移除同步屏障。
注释2:调用performTraversals()函数执行View的绘制流程。
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);
// 测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
// 如果是LinearLayout且设置了权重,会再次测量
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(mTag,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
// Not the first pass and no window/insets/visibility change but the window
// may have moved and we need check that and if so to update the left and right
// in the attach info. We translate only the window frame since on window move
// the window manager tells us only for the new frame but the insets are the
// same and we do not want to translate them more than once.
maybeHandleWindowMove(frame);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
// 确定View的位置
performLayout(lp, mWidth, mHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
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);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
host.debug();
}
}
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
// Tell the window manager.
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);
// Translate insets to screen coordinates if needed.
final Rect contentInsets;
final Rect visibleInsets;
final Region touchableRegion;
if (mTranslator != null) {
contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion);
} else {
contentInsets = insets.contentInsets;
visibleInsets = insets.visibleInsets;
touchableRegion = insets.touchableRegion;
}
try {
mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
if (mFirst && sAlwaysAssignFocus) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
+ mView.hasFocus());
if (mView != null) {
if (!mView.hasFocus()) {
mView.restoreDefaultFocus();
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: requested focused view="
+ mView.findFocus());
} else {
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: existing focused view="
+ mView.findFocus());
}
}
}
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
if (regainedFocus) {
mLostWindowFocus = false;
} else if (!hasWindowFocus && mHadWindowFocus) {
mLostWindowFocus = true;
}
if (changedVisibility || regainedFocus) {
// Toasts are presented as notifications - don't present them as windows as well
boolean isToast = (mWindowAttributes == null) ? false
: (mWindowAttributes.type == WindowManager.LayoutParams.TYPE_TOAST);
if (!isToast) {
host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
}
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mActivityRelaunched = false;
mViewVisibility = viewVisibility;
mHadWindowFocus = hasWindowFocus;
if (hasWindowFocus && !isInLocalFocusMode()) {
final boolean imTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
}
}
// Remember if we must report the next draw.
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
// 绘制
performDraw();
} else {
if (isViewVisible) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
}
这里就不详细讲了,这篇文章的重点不在这,如有不懂可以看《View绘制流程》。
总结
屏幕每16毫秒会刷新一次,View想要绘制就要向底层请求Vsync信号,当下一个Vsync信号来的时候会通过回调通知到View,View就可以执行绘制流程。
导致掉帧的原因一般有两种:
- 主线程有耗时操作,当Vsync信号来的时候,不能及时执行doFrame()函数。
- doFrame()函数执行耗时,没有在Vsync信号来的时候去绘制。