View绘制机制

作者: Chase_stars | 来源:发表于2019-10-19 17:07 被阅读0次

    心是被委屈撑大的,优秀是用痛苦磨成的。— 《等一个人读书》

    写在前面

    作为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信号来的时候去绘制。

    相关文章

      网友评论

        本文标题:View绘制机制

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