Android渲染繪制機(jī)制

Android應(yīng)用層是不涉及SurfaceFlinger,FrameBuffer之類的底層框架,常用刷新視圖都是在Viewdraw相關(guān)方法中進(jìn)行標(biāo)準(zhǔn)繪制api操作,然后通過(guò)View.invalidate或者View.requestLayout通知系統(tǒng)進(jìn)行視圖顯示的刷新。在此不討論draw相關(guān)的api,draw的所有繪制方法都是直接jni調(diào)用對(duì)應(yīng)skia的繪制,具體的自己查看skia引擎相關(guān)的資料。
其實(shí)View.invalidate或者View.requestLayout最終走向的流程基本一致,都是一層層向上遍歷,最終進(jìn)入視圖管理器ViewRootImpl中進(jìn)行下一次的vsync信號(hào)請(qǐng)求,在接收到信號(hào)后進(jìn)行視圖渲染的刷新。
1.簡(jiǎn)單分析下View.invalidate:
1.1 比如自定義View動(dòng)畫,通常都是根據(jù)屬性動(dòng)畫進(jìn)度計(jì)算出此時(shí)刻需要展示的效果,然后進(jìn)行invalidate或者postInvalidate進(jìn)行刷新。
        anim.addUpdateListener {
            val percent: Float = it.animatedValue as Float
            curDistance = maxDistance * percent
            invalidate()// 重繪
        }

        override fun onDraw(canvas: Canvas?) {
            canvas!!.drawLine(0F, paint.strokeWidth, curDistance, paint.strokeWidth, paint)
        }        
1.2 那就從invalidate開始分析:
<View.java>
    // invalidate最終調(diào)用的是invalidateInternal
    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
        if (mGhostView != null) {// 幽靈視圖,可以理解為一個(gè)狀態(tài)不可見view的拷貝,類似overlay圖層概念
            mGhostView.invalidate(true);
            return;
        }

        if (skipInvalidate()) {// 不需要重繪,return
            return;
        }

        // Reset content capture caches
        mCachedContentCaptureSession = null;

        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
                || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
            if (fullInvalidate) {// 全局刷新
                mLastIsOpaque = isOpaque();
                mPrivateFlags &= ~PFLAG_DRAWN;
            }

            mPrivateFlags |= PFLAG_DIRTY;

            if (invalidateCache) {// 視圖緩存相關(guān)標(biāo)記
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }

            // Propagate the damage rectangle to the parent view.
            final AttachInfo ai = mAttachInfo;// attachInfo之前分析過(guò),整個(gè)視圖樹共享
            final ViewParent p = mParent;// 父View
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
                p.invalidateChild(this, damage);// 通知父ViewGroup重繪自己
            }
            // 下面的damageInParent是新的api,暫時(shí)還是分析上面invalidateChild方法的重繪流程,流程最終走向都是一致的

            // Damage the entire projection receiver, if necessary.
            if (mBackground != null && mBackground.isProjected()) {
                final View receiver = getProjectionReceiver();
                if (receiver != null) {
                    receiver.damageInParent();
                }
            }
        }
    }
  • invalidate最終調(diào)用invalidateInternal,內(nèi)部會(huì)通知父View對(duì)自己進(jìn)行重繪,看一下ViewGroup.invalidateChild方法
<ViewGroup.java>
    @Override
    public final void invalidateChild(View child, final Rect dirty) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null && attachInfo.mHardwareAccelerated) {
            // HW accelerated fast path
            onDescendantInvalidated(child, child);// 硬件加速繪制
            return;
        }

        ViewParent parent = this;
        if (attachInfo != null) {
            // If the child is drawing an animation, we want to copy this flag onto
            // ourselves and the parent to make sure the invalidate request goes
            // through
            final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;

            // Check whether the child that requests the invalidate is fully opaque
            // Views being animated or transformed are not considered opaque because we may
            // be invalidating their old position and need the parent to paint behind them.
            Matrix childMatrix = child.getMatrix();
            // Mark the child as dirty, using the appropriate flag
            // Make sure we do not set both flags at the same time

            if (child.mLayerType != LAYER_TYPE_NONE) {
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }
            // 計(jì)算需要重繪view的繪制區(qū)域
            final int[] location = attachInfo.mInvalidateChildLocation;
            location[CHILD_LEFT_INDEX] = child.mLeft;
            location[CHILD_TOP_INDEX] = child.mTop;
            if (!childMatrix.isIdentity() ||
                    (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
                RectF boundingRect = attachInfo.mTmpTransformRect;
                boundingRect.set(dirty);
                Matrix transformMatrix;
                if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
                    Transformation t = attachInfo.mTmpTransformation;
                    boolean transformed = getChildStaticTransformation(child, t);
                    if (transformed) {
                        transformMatrix = attachInfo.mTmpMatrix;
                        transformMatrix.set(t.getMatrix());
                        if (!childMatrix.isIdentity()) {
                            transformMatrix.preConcat(childMatrix);
                        }
                    } else {
                        transformMatrix = childMatrix;
                    }
                } else {
                    transformMatrix = childMatrix;
                }
                transformMatrix.mapRect(boundingRect);
                dirty.set((int) Math.floor(boundingRect.left),
                        (int) Math.floor(boundingRect.top),
                        (int) Math.ceil(boundingRect.right),
                        (int) Math.ceil(boundingRect.bottom));
            }
            // 循環(huán)向上遍歷到視圖樹結(jié)構(gòu)的根節(jié)點(diǎn)ViewRootImpl
            do {
                View view = null;
                if (parent instanceof View) {
                    view = (View) parent;
                }

                if (drawAnimation) {
                    if (view != null) {
                        view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                    } else if (parent instanceof ViewRootImpl) {
                        ((ViewRootImpl) parent).mIsAnimating = true;
                    }
                }

                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
                // flag coming from the child that initiated the invalidate
                if (view != null) {
                    if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
                        view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
                    }
                }

                parent = parent.invalidateChildInParent(location, dirty);
                if (view != null) {
                    // Account for transform on current parent
                    Matrix m = view.getMatrix();
                    if (!m.isIdentity()) {
                        RectF boundingRect = attachInfo.mTmpTransformRect;
                        boundingRect.set(dirty);
                        m.mapRect(boundingRect);
                        dirty.set((int) Math.floor(boundingRect.left),
                                (int) Math.floor(boundingRect.top),
                                (int) Math.ceil(boundingRect.right),
                                (int) Math.ceil(boundingRect.bottom));
                    }
                }
            } while (parent != null);
        }
    }
1.3 簡(jiǎn)單分析下流程吧,如果是硬件加速,會(huì)直接進(jìn)入繪制流程(其實(shí)最終走向也是一樣的),否則會(huì)通過(guò)一系列計(jì)算出臟區(qū),遍歷到視圖樹的根節(jié)點(diǎn),重點(diǎn)看這行代碼parent = parent.invalidateChildInParent(location, dirty);,視圖樹的根節(jié)點(diǎn),也就是ViewRootImpl,所以直接看ViewRootImpl.invalidateChildInParent方法。繪制細(xì)節(jié)不是本文重點(diǎn)分析,重點(diǎn)分析的是整個(gè)渲染流程,細(xì)節(jié)部門自行查看。
<ViewRootImpl.java>
    @Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();// 檢測(cè)線程
        if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);

        if (dirty == null) {// 臟區(qū)為null,全局重繪
            invalidate();
            return null;
        } else if (dirty.isEmpty() && !mIsAnimating) {
            return null;
        }
        // 加入滾動(dòng),平移參數(shù)計(jì)算出真正的臟區(qū)
        if (mCurScrollY != 0 || mTranslator != null) {
            mTempRect.set(dirty);
            dirty = mTempRect;
            if (mCurScrollY != 0) {
                dirty.offset(0, -mCurScrollY);
            }
            if (mTranslator != null) {
                mTranslator.translateRectInAppWindowToScreen(dirty);
            }
            if (mAttachInfo.mScalingRequired) {
                dirty.inset(-1, -1);
            }
        }
        // 臟區(qū)重繪
        invalidateRectOnScreen(dirty);
        // 當(dāng)前已經(jīng)是根節(jié)點(diǎn),return null退出ViewGroup遍歷循環(huán)
        return null;
    }

    // 全局重繪 
    @UnsupportedAppUsage
    void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            scheduleTraversals();// 這就是渲染流程的真正開始
        }
    }

    // 臟區(qū)重繪(高效)
    private void invalidateRectOnScreen(Rect dirty) {
        final Rect localDirty = mDirty;

        // Add the new dirty rect to the current one
        localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
        // Intersect with the bounds of the window to skip
        // updates that lie outside of the visible region
        final float appScale = mAttachInfo.mApplicationScale;
        final boolean intersected = localDirty.intersect(0, 0,
                (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
        if (!intersected) {
            localDirty.setEmpty();
        }
        if (!mWillDrawSoon && (intersected || mIsAnimating)) {
            scheduleTraversals();// 這就是渲染流程的真正開始
        }
    }
1.4 上面的流程很明了,不論是全局刷新還是局部刷新,最終都是走向scheduleTraversals,所以可以說(shuō)scheduleTraversals方法是上層發(fā)起重繪的起點(diǎn)。(硬件加速其實(shí)也是一樣走向scheduleTraversals方法,只是在底層繪制上有所區(qū)別)
2.View.requestLayout流程基本一致,也是通過(guò)向上遍歷,最終調(diào)用視圖樹根節(jié)點(diǎn)的``方法:
<ViewRootImpl.java>
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();// 檢測(cè)線程
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
3.到這里先暫停下,補(bǔ)充個(gè)知識(shí)點(diǎn),面試常見的坑:為何只能主線程刷新UI?
  • 上面不論是invalidata還是requestLayout,方法內(nèi)部的第一行代碼都是checkThread,這里面就有常見的異常打印:
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            // 熟悉的文字
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
  • Android禁止主線程刷新UI,其實(shí)就是在ViewRootImpl中所有涉及UI操作方法中判斷非當(dāng)前線程主動(dòng)拋出異常而已,典型的強(qiáng)制措施(其實(shí)也是為了能保證主線程的同步性可靠性,要是大家都在子線程刷新UI,最終合成渲染圖層豈不是畫面凌亂了?)
  • 所以本質(zhì)上通過(guò)反射,或者在ViewRootImpl未初始化前,都是可以在子線程刷新UI。這也是為何在Activity.onCreate方法中可以子線程刷新UI不會(huì)崩潰的原因。
4.題外話說(shuō)多了,現(xiàn)在正式開始分析渲染流程:
4.1 首先介紹一下Traversal相關(guān)幾個(gè)方法:scheduleTraversals -> doTraversal -> performTraversals,這幾個(gè)方法的執(zhí)行順序是schedule -> do -> perform,可以理解為計(jì)劃準(zhǔn)備階段->準(zhǔn)備執(zhí)行階段->完成階段
4.2 scheduleTraversals分析:
<ViewRootImpl.java>
    @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {// 這里會(huì)限制重入,一般情況16ms你不論調(diào)用多少次invalidate或者requestLayout,最終效果都是一樣
            mTraversalScheduled = true;// 重入限制
            // 消息屏障,異步消息,之前handler章節(jié)分析過(guò)
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();// 發(fā)送消息屏障,保證優(yōu)先級(jí)
            mChoreographer.postCallback(// 請(qǐng)求下一次Vsync信號(hào)
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            // 相關(guān)通知回調(diào)
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

    // 請(qǐng)求Vsync的時(shí)候傳遞了這個(gè)參數(shù)mTraversalRunnable
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    // 非常簡(jiǎn)單的一個(gè)runnable
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();// 執(zhí)行階段,下面分析
        }
    }
  • 內(nèi)部通過(guò)一個(gè)mTraversalScheduled變量限制重入,所以一般情況16ms你不論調(diào)用多少次invalidate或者requestLayout,最終效果都是一樣,并且你調(diào)用之后并不是立即就執(zhí)行重繪,后面分析。這里還涉及到異步消息,之前分析過(guò),不具體分析,簡(jiǎn)單來(lái)說(shuō)就是往消息隊(duì)列插入一條異步消息作為屏障,插入屏障之后消息隊(duì)列的同步消息停止執(zhí)行,直到該消息屏障移除后才恢復(fù),主要就是為了保證優(yōu)先級(jí),畢竟交互響應(yīng)是優(yōu)先級(jí)最高的。這里還涉及到Choreographer編舞者的角色,主要是解決幀率不同步,掉幀問題,非本文重點(diǎn),本文只分析下其內(nèi)部對(duì)于vsync請(qǐng)求流程和回調(diào)時(shí)機(jī)。
<Choreographer.java>
    // 請(qǐng)求Vsync信號(hào),postCallback最終會(huì)走到這里
    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添加到緩存隊(duì)列
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
            if (dueTime <= now) {
                // 分支1:需要立即執(zhí)行回調(diào)
                scheduleFrameLocked(now);
            } else {
                // 分支2:還未到需要的執(zhí)行時(shí)間,在指定的時(shí)間發(fā)送異步消息,保證回調(diào)執(zhí)行的優(yōu)先級(jí)
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);// 指定了需要執(zhí)行的時(shí)間
            }
        }
    }

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {// 重入限制
            mFrameScheduled = true;
            if (USE_VSYNC) {// 默認(rèn)true
                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()) {// 當(dāng)前如果是主線程
                    scheduleVsyncLocked();// vsync準(zhǔn)備階段
                } else {// 場(chǎng)景1:線程切換,直接插入一條消息到隊(duì)頭
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else { // 場(chǎng)景2:發(fā)送消息
                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);
            }
        }
    }

    // 上面的mHandler是FrameHandler,屬于主線程handler,具體實(shí)例化過(guò)程不分析
    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:// scheduleFrameLocked的場(chǎng)景2
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC:// scheduleFrameLocked的場(chǎng)景1
                    doScheduleVsync();// 最終還是走到scheduleVsyncLocked,場(chǎng)景1只是多了一步線程切換
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:// postCallbackDelayedInternal的分支2
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

    // 使用vsync的場(chǎng)景1
    void doScheduleVsync() {
        synchronized (mLock) {
            if (mFrameScheduled) {
                scheduleVsyncLocked();
            }
        }
    }

    // 最終流程和postCallbackDelayedInternal的分支1一致,只是消息延遲點(diǎn)執(zhí)行而已
    void doScheduleCallback(int callbackType) {
        synchronized (mLock) {
            if (!mFrameScheduled) {
                final long now = SystemClock.uptimeMillis();
                if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                    scheduleFrameLocked(now);
                }
            }
        }
    }

    // 請(qǐng)求vsync
    @UnsupportedAppUsage
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

    // doFrame關(guān)鍵方法
    // 1.非使用vsync會(huì)直接執(zhí)行該方法,也就是直接出發(fā)重繪回調(diào)
    // 2.使用vsync會(huì)等下一次vsync信號(hào)來(lái)到時(shí),觸發(fā)重繪回調(diào)
    @UnsupportedAppUsage
    void doFrame(long frameTimeNanos, int frame) {
        ...// 后面分析
    }
  • ViewRootImpl請(qǐng)求vsync信號(hào)的時(shí)候,會(huì)傳入一個(gè)runnable消息,Choreographer將這個(gè)消息存放到隊(duì)列中,并且根據(jù)當(dāng)前時(shí)間,決定是立即安排vsync計(jì)劃還是延時(shí)(scheduleFrameLocked(now)和MSG_DO_SCHEDULE_CALLBACK),本質(zhì)上最終調(diào)用scheduleFrameLocked(long now)方法。
  • scheduleFrameLocked(long now)中有兩種分支,一種使用vsync機(jī)制,一種非使用vsync機(jī)制。他們的區(qū)別就是使用該同步機(jī)制,會(huì)在下一次vsync信號(hào)到來(lái)時(shí)進(jìn)行刷新,否則立即刷新(doFrame方法)。下面分析下請(qǐng)求vsync的流程:
<Choreographer.java>
    // 請(qǐng)求vsync
    @UnsupportedAppUsage
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();// 請(qǐng)求vsync信號(hào)
    }

<DisplayEventReceiver.java>
    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 {
            // native請(qǐng)求vsync信號(hào),當(dāng)?shù)讓影l(fā)送vsync信號(hào)時(shí),java層就能接收到通知
            nativeScheduleVsync(mReceiverPtr);
        }
    }

    // 當(dāng)?shù)讓影l(fā)送vsync信號(hào)時(shí)會(huì)調(diào)用這個(gè)java方法
    // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        // 回調(diào)onVsync方法
        onVsync(timestampNanos, physicalDisplayId, frame);
    }
  • 其實(shí)請(qǐng)求vsync過(guò)程很簡(jiǎn)單,就是通過(guò)jni向底層注冊(cè)一個(gè)回調(diào)(構(gòu)造內(nèi)會(huì)保存c++層Receiver引用的指針地址),底層發(fā)送vsync時(shí)候,反向調(diào)用java方法(onVsync)通知上層。DisplayEventReceiver是一個(gè)抽象類,在Choreographer中可以找到一個(gè)具體實(shí)現(xiàn)的內(nèi)部類FrameDisplayEventReceiver:
<Choreographer.java>
    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);
        }

        // 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.
        // 底層主動(dòng)調(diào)用該方法
        @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;
            // 發(fā)送異步消息,因?yàn)樽约簩?shí)現(xiàn)了runnable,所以是把自己當(dāng)成消息發(fā)送出去,看下面的run方法
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            // 執(zhí)行doFrame方法進(jìn)行重繪
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }
  • 下面就到關(guān)鍵的方法doFrame了,這里你也會(huì)看到很多常見的log打印信息:
    @UnsupportedAppUsage
    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {// 上鎖
            if (!mFrameScheduled) {// 這個(gè)標(biāo)記位就是最初scheduleFrameLocked開始限制重入那個(gè)
                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;// 本次vsync時(shí)間
            startNanos = System.nanoTime();// 開始執(zhí)行doFrame時(shí)間
            final long jitterNanos = startNanos - frameTimeNanos;// jitterNanos = doFrame - Vsync 的時(shí)間差
            // mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
            if (jitterNanos >= mFrameIntervalNanos) {// 假設(shè)幀率為60fps,mFrameIntervalNanos為通常所說(shuō)的16ms
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;// 計(jì)算跳幀數(shù)
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {// >30fps
                    // 熟悉的掉幀打印信息
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                            + "The application may be doing too much work on its main thread.");
                }
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;// doFrame延遲n個(gè)周期后取余的時(shí)間
                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.");
                }
                // 修正vsync的到來(lái)時(shí)間
                frameTimeNanos = startNanos - lastFrameOffset;
                // lastFrameOffset = jitterNanos % mFrameIntervalNanos
                // frameTimeNanos = startNanos - lastFrameOffset = startNanos - (jitterNanos % 16) = startNanos - (startNanos - frameTimeNanos) % 16
                // 所以 frameTimeNanos = 當(dāng)前doFrame時(shí)間之前最近的一個(gè)vsync時(shí)間
            }
            // 避免下一幀提前渲染,如果本次vsync執(zhí)行doFrame比上一幀計(jì)劃的提交時(shí)間早,則將本幀放到下一個(gè)vsync進(jìn)行渲染
            // mLastFrameTimeNanos在修正過(guò)程可能出現(xiàn)這種場(chǎng)景
            // 提前渲染就會(huì)出現(xiàn)畫面重疊重影現(xiàn)象
            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.");
                }
                // 重新請(qǐng)求下一次vsync信號(hào),此刻mFrameScheduled沒有重置false,外部調(diào)用的scheduleFrameLocked(now)不再執(zhí)行,也就是此時(shí)外部postCallback也是無(wú)效的
                // 請(qǐng)求下一次vsync信號(hào)->doFrame,如果還不滿足條件,重復(fù)如此
                scheduleVsyncLocked();
                return;
            }
            // 默認(rèn)1,不用管
            if (mFPSDivisor > 1) {
                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                    scheduleVsyncLocked();
                    return;
                }
            }
            // 記錄當(dāng)前幀的原始vsync時(shí)間-修正后的vsync時(shí)間
            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;// 重置標(biāo)記位,可以再次進(jìn)入scheduleFrameLocked
            // 記錄上一次vsync的時(shí)間
            mLastFrameTimeNanos = frameTimeNanos;
        }
        // 開始執(zhí)行各種callback
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
            // 輸入
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
            // 動(dòng)畫
            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
            // 遍歷:measure,layout,draw
            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
            // 遍歷完成提交,修復(fù)下一幀的提交時(shí)間,保證和vsync節(jié)奏同步
            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.");
        }
    }
  • 上面可以看到接收到了vsync信號(hào)后會(huì)先判斷是否掉幀(執(zhí)行doFrame時(shí)間比Vsync的時(shí)間延遲),打印出掉幀信息,再進(jìn)行渲染刷新,之前ViewRootImpl.scheduleTraversals方法中通過(guò)mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)明確這個(gè)callback的類型Choreographer.CALLBACK_TRAVERSAL,所以doFrame中我們重點(diǎn)分析該類型的回調(diào):
<Choreographer.java>

    void doFrame(long frameTimeNanos, int frame) {   
        ......
        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
        ......
    }

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;// 單鏈表結(jié)構(gòu)
        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();
            // 從隊(duì)列中取出callback鏈表,包含我們之前scheduleTraversals傳進(jìn)來(lái)的callback
            // CallbackQueue為一個(gè)子元素為鏈表的數(shù)組隊(duì)列,里面每一種callback類型都是一個(gè)CallbackRecord的單鏈表
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(// 取出執(zhí)行時(shí)間在當(dāng)前時(shí)間之前的callback
                    now / TimeUtils.NANOS_PER_MS);
            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.
            if (callbackType == Choreographer.CALLBACK_COMMIT) {
                // 進(jìn)入這個(gè)分支后,now = 執(zhí)行完動(dòng)畫,繪制一系列操作之后的當(dāng)前時(shí)間
                // 提交刷新,修正時(shí)間,同步vsync的節(jié)奏
                final long jitterNanos = now - frameTimeNanos;
                Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                if (jitterNanos >= 2 * mFrameIntervalNanos) {// > 2*16 = 32ms(60fps為例)
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos// frameTimeNanos = startNanos - lastFrameOffset,now是執(zhí)行玩measure-layout-draw的時(shí)間
                            + mFrameIntervalNanos;// mFrameIntervalNanos=16ms
                    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;
                    }
                    // 修正時(shí)間 mLastFrameTimeNanos = frameTimeNanos = 從now往前最近的一個(gè)vsync時(shí)間
                    frameTimeNanos = now - lastFrameOffset;
                    mLastFrameTimeNanos = frameTimeNanos;
                }
            }
            // 大致總結(jié)下:在一幀處理過(guò)程,如果超過(guò)了n>=2個(gè)vsync周期,則會(huì)在接下來(lái)n個(gè)vsync周期中不再處理任何幀,下一幀會(huì)在n個(gè)周期后對(duì)齊vsync信號(hào)時(shí)開始處理,相當(dāng)于中途拋棄n幀畫面,達(dá)到盡可能幀率平穩(wěn),與vsync同步
        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            // 循環(huán)執(zhí)行callback的run方法
            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);
            }
        } finally {
            // 資源釋放
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
  • 邏輯很簡(jiǎn)單,從緩存隊(duì)列取出之前傳入的callback,調(diào)用其run方法,也就是:
<ViewRootImpl.java>
    final class TraversalRunnable implements Runnable {
        // 就是這個(gè)run方法
        @Override
        public void run() {
            doTraversal();
        }
    }
4.2 至此已經(jīng)分析完了scheduleTraversals請(qǐng)求vsync的過(guò)程,下面開始分析渲染刷新doTraversal->performTraversal:
<ViewRootImpl.java>
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;// 走到這里,放開了重入,這個(gè)時(shí)候外部調(diào)用invalidate之類請(qǐng)求重繪才會(huì)生效
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);// 移除消息屏障,主線程同步消息恢復(fù)運(yùn)轉(zhuǎn)
            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            // 核心方法,View的layout,measure,draw相關(guān)方法都是在這里面執(zhí)行的,簡(jiǎn)單描述下流程,細(xì)節(jié)不展開了(否則沒玩沒了,這個(gè)方法巨長(zhǎng),800多行代碼)
            performTraversals();

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

    private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;// 其實(shí)就是DecorView
        ......

        if (host == null || !mAdded)// DecorView沒初始化和添加到window,直接return
            return;

        mIsInTraversal = true;// 標(biāo)記正在執(zhí)行遍歷
        mWillDrawSoon = true;// 標(biāo)記立即繪制
        ......
        if (mFirst) {// 初始化第一次的時(shí)候
            ......
            // OnAttachedToWindow回調(diào)
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
            dispatchApplyInsets(host);
        } else {
            ......
        }

        ......

        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        // 這個(gè)在之前View.post原理的文章中已經(jīng)分析過(guò)
        getRunQueue().executeActions(mAttachInfo.mHandler);

        ......

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

                    ......

                    // 開始view的測(cè)量
                     // Ask host how big it wants to be
                    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;
                    }

                    if (measureAgain) {
                        if (DEBUG_LAYOUT) Log.v(mTag,
                                "And hey let's measure once more: width=" + width
                                + " height=" + height);
                        // 需要多次測(cè)量的話,再次進(jìn)行view的測(cè)量,所以有的viewgroup會(huì)測(cè)量?jī)纱?                        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);
        }

        if (surfaceSizeChanged) {
            updateBoundsSurface();
        }

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            // ViewGroup進(jìn)行布局子View
            performLayout(lp, mWidth, mHeight);

            ......
        }

        ......

        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
        // 不可見或者正在繪制,就不需要繪制了
        if (!cancelDraw) {
            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).startChangingAnimations();
                }
                mPendingTransitions.clear();
            }
            // 遍歷繪制
            performDraw();
        } else {
            ......
        }

        mIsInTraversal = false;
    }
  • 總結(jié)一下,performTraversals中調(diào)用了performMeasure->performLayout->performDraw View的三大流程,三大流程內(nèi)部都是通過(guò)遍歷子view,遍歷調(diào)用我們熟悉的onMeasure->onLayout->onDraw回調(diào),源碼的邏輯很清晰,在此不分析了。
4.3 至此,doTraversalperformTraversals分析完了。我們應(yīng)用中的draw之類的api調(diào)用其實(shí)都是在操作底層skia引擎對(duì)應(yīng)的SkiaCanvas畫布,在framework層對(duì)應(yīng)存在一塊buffer保存圖元數(shù)據(jù),最終通過(guò)SurfaceFlinger進(jìn)行圖層合并處理,以及顏色矩陣運(yùn)算(Android原生的護(hù)眼模式就是這部分操作的,在最終渲染畫面前通過(guò)顏色矩陣運(yùn)算改變顯示輸出色溫)等一系列操作,然后提交給GPU處理渲染到屏幕硬件上,SurfaceFlinger是系統(tǒng)的圖形管理服務(wù)(純c++服務(wù),不像AMS,PMS,WMS),核心流程是下面幾個(gè)方法,應(yīng)用開發(fā)可以不需要過(guò)多關(guān)注,感興趣的自行閱讀。
<SurfaceFlinger.cpp>
// SurfaceFlinge中圖層渲染合成關(guān)鍵流程方法
void SurfaceFlinger::handleMessageRefresh() {
    ATRACE_CALL();
    preComposition();
    rebuildLayerStacks();
    setUpHWComposer();
    doDebugFlashRegions();
    doComposition();
    postComposition();
}
最后用一張圖總結(jié)下整個(gè)流程:
渲染流程.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容