天天看点

View 绘制流程和刷新机制1. 前言2. 绘制流程3. 屏幕刷新机制4. 总结

1. 前言

当 Activity 启动时候,会调用到 ActivityThread 的 handleResumeActivity 方法。在了解此篇文章的时候,先了解 Activity 、 Window 、 View 之间的关系,从这里可以了解到 PhoneWindow、DecorView 和 ViewGroup/View 的关系。此篇文章将从 ActivityThread 中的 handleResumeActivity 开始解析。

2. 绘制流程

在 ActivityThread 的 handleResumeActivity 中:

@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...


        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        if (r == null) {
            // We didn't actually resume the activity, so skipping any follow-up actions.
            return;
        }
        final Activity a = r.activity;


        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);    // 1
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }

        ...
    }
           

这里的核心代码 wm.addView(decor, l), vm 是 WindowManagerImpl,decor 是 DecorView。其中 WindowManagerImpl 的继承关系如图所示:

View 绘制流程和刷新机制1. 前言2. 绘制流程3. 屏幕刷新机制4. 总结

WindowManagerImpl 中 addView 方法:

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
           

调用 WindowManagerGlobal 的 addView 方法,这里的 view 是前面传递进来的 decorView,addView 方法如下:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...


        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ...

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
           

此方法中创建了 ViewRootImpl 对象,并且调用 ViewRootImpl 的  setView 方法,并且将 decorView 传递进去,这样 ViewRootImpl 就跟 decorView 联系起来了,ViewRootImpl 的继承关系如下:

View 绘制流程和刷新机制1. 前言2. 绘制流程3. 屏幕刷新机制4. 总结

setView 方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;

                ...


                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
                
                ...

            }
        }
    }
           

此方法中的核心是调用了 requestLayout 方法:

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
           

接着调用了 scheduleTraversals 方法:

@UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
           

通过调用 Choreographer 启动一个TraversalRunnable:

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
           

接着调用 doTraversal 方法:

void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
           

接着调用 performTraversals 方法:

private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
    
    ...

    if (!mStopped || mReportNextDraw) {
    
        ...
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        ...
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
    }

    ...

    if (didLayout) {
        performLayout(lp, mWidth, mHeight);
        ...
    }

    ...
    performDraw();
    ...

}
           

核心就是三个方法:performMeasure、performLayout、performDraw。

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);
        }
    }
           

这里的 mView 是前面传递过来的 DecorView,DecorView 是 FrameLayout ,本质是一个 ViewGroup。所以一开始执行的是 ViewGroup 的 measure 方法,并且按照以下的流程逐一对子 View 进行测量:

View 绘制流程和刷新机制1. 前言2. 绘制流程3. 屏幕刷新机制4. 总结

这里以 DecorView 为例子,在 DecorView 和 ViewGroup 中是找不到 measure 方法,measure 方法的实现在父类 View 中:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        ...


        if (forceLayout || needsLayout) {
            ...
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                ...
            }
            ...

            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }
        ...
    }
           

这里的核心方法是调用了自己本身的 onMeasure 方法,对于 DecorView 来讲,onMeasure 方法的实现在 FrameLayout 中:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();

        ...

        count = mMatchParentChildren.size();
        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);
                
                ...

                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }
           

如果子 View 的个数大于0,则又调用了子 View 的 measure 方法,整体逻辑符合上面的流程图。

3. 屏幕刷新机制

Android 屏幕每16毫秒会刷新一次,也就是每秒会刷新60次,人眼能感觉到卡顿的帧率是每秒24帧。要求我们的应用都能在 16ms 内绘制完成。如果有一次的界面绘制用了 22ms,那么,用户在 32ms 内看见的都是同一个界面。就会让用户觉得卡顿。而 Android 是如何进行刷新的呢?

在上面的流程中,方法 scheduleTraversals:

@UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//1
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//2
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
           

在上面的分析中,并没有具体分析这里面执行的方法,接下来将具体分析下。

注释1:设置了同步屏障,这里的作用是为了更快的响应 Ui 刷新事件。设置了同步屏障之后,同步屏障为 Handler 消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。关于同步屏障,可以参考Handler之同步屏障机制(sync barrier)。

在注释 2 中调用了 Choreographer 的 postCallback 方法:

@UnsupportedAppUsage
    @TestApi
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }
           

 postCallbackDelayed 方法,这里的参数 delayMillis 传递 0 进来:

@UnsupportedAppUsage
    @TestApi
    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);
    }
           

postCallbackDelayedInternal 方法: 

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);

            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);
            }
        }
    }
           

由于传递进来的 delayMillis 为0,则执行 scheduleFrameLocked 方法:

private void scheduleFrameLocked(long now) {
        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.
                if (isRunningOnLooperThreadLocked()) {    // 3
                    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);
            }
        }
    }
           

 注释3:判断当前线程是 UI线程,执行 scheduleVsyncLocked 方法:

@UnsupportedAppUsage
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }
           

调用 DisplayEventReceiver 的 scheduleVsync 方法 ,DisplayEventReceiver 是一个抽象类,FrameDisplayEventReceiver 是它的实现类:

/**
     * Schedules a single vertical sync pulse to be delivered when the next
     * display frame begins.
     */
    @UnsupportedAppUsage
    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);
        }
    }
           

 这里调用了一个 native层的方法,此方法的作用:通知底层,下一个 VSync 信号来的时候请通知我,当 VSync 信号来的时候,就会收到底层的 JNI 回调,会调用到 dispatchVsync 方法:

// Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        onVsync(timestampNanos, physicalDisplayId, frame);
    }
           

onVsync 方法的实现在 Choreographer 的内部类 FrameDisplayEventReceiver 中:

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, CONFIG_CHANGED_EVENT_SUPPRESS);
        }

        // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
        // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
        // for the internal display implicitly.
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            // 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();
            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;
            }

            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);
        }
    }
           

当接收到信号时候,就会执行 run 方法中的 doFrame 方法:

@UnsupportedAppUsage
    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            ...

            long intendedFrameTimeNanos = frameTimeNanos;//设置当前帧的Vsync信号到来时间
            startNanos = System.nanoTime();//实际开始执行当前帧的时间
            final long jitterNanos = startNanos - frameTimeNanos;
            if (jitterNanos >= mFrameIntervalNanos) {//时间差大于一个时钟周期,认为跳帧
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                            + "The application may be doing too much work on its main thread.");
                }
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                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;
            }

            if (mFPSDivisor > 1) {
                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                    scheduleVsyncLocked();//申请下一次vsync信号,流程跟上面分析一样
                    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);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        ...
    }
           

计算收到 VSync 信号到 doFrame 被调用的时间差,VSync 信号间隔是16毫秒一次,大于16毫秒就是掉帧。如果超过30帧(默认30),就打印log提示开发者检查主线程是否有耗时操作。

接着执行 doCallbacks 方法:

void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {

            final long now = System.nanoTime();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;

            ...

        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            for (CallbackRecord c = callbacks; c != null; c = c.next) {

                ...

                c.run(frameTimeNanos);
            }
        } finally {
            ...
        }
    }
           

 这里的核心的就是取出队列中任务,也就是 doTraversal 方法。

4. 总结

当 Activity 启动时候,会调用到 ActivityThread 的 handleResumeActivity 方法,接着调用 ViewRootImpl 的 setView 方法。setView  方法中会调用 requestLayout,requestLayout 方法中调用 scheduleTraversals,接着 doTraversal ,最终调用到了 performTraversals。接着分别执行 performMeasure、performLayout、performDraw 方法。performMeasure 方法中首先会执行 measure 方法,接着执行 onMeasure 方法。在 onMeasure 方法中如果当前 View 存在子 View,则遍历执行子 View 的 measure ,最终完成测量的工作。performLayout 和 performDraw 的逻辑也一样。

在 scheduleTraversals 方法中,通过 Choreographer 类向 native 请求 VSync(垂直同步)信号,下一次 VSync(垂直同步)信号来的时候通过 JNI 调用 onVsync 方法通知应用层,并把消息发送到主线程,请求执行 doFrame 渲染下一帧。