2 Android显示系统之View体系的渲染

围巾🧣 2023年10月13日 336次浏览

内容

image-20231012145014027

概述

众所周知, 在Android中每⼀个Activity的展示的载体都是PhoneWindow, 包括顶部的状态栏与底 部的导航栏也都是系统的Window。 而Window作为ViewTree的载体, 内部的展示由ViewTree来实 现。所以展示的组织结构是这样的:

image-20231012145649534

由上图可⻅, 在Android中View管理的数据结构是典型的树形结构, ⽽所有树形结构的根节点 就是ViewRoot; 然⽽ViewRoot并不是⼀个View, 它本⾝只是⼀个ViewParent, 负责作为 WindowManager与View交互的中间⻆⾊。

View体系的渲染包括两部分: View变更申请Vsync信号与Vsync接收重新构建渲染树。

  • View变更申请Vsync信号 : 从发⽣变更的叶⼦节点向根节点回溯, 将Travales加⼊到 callback队列, 并请求下⼀次Vsync信号

  • Vsync接收重新构建渲染 : 从ViewRoot开始, 向下遍历ViewTree; 软件渲染则通过 View.draw进⾏重绘, 最后通过unlockCanvasAndPost通知SurfaceFlinger合成; 硬件渲染 通过updateDisplayListIfDirty更新渲染树, 通过syncAndDrawFrame通知RenderThread 绘制。

requestLayout

View体系的渲染

View.requestLayout是从发⽣变更的View开始, 先让MeasureCache失效, 再对当前View打上PFLAG_FORCE_LAYOUTPFLAG_INVALIDATED标识; 然后不断调⽤mParent.requestLayout按 深度标记整个⼦树。

    public void requestLayout() {
        // note jin: 先让MeasureCache失效, 再对当前View打上
        // PFLAG_FORCE_LAYOUT 和 PFLAG_INVALIDATED标识
        if (mMeasureCache != null) mMeasureCache.clear();

        ……

        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;

        // note jin: 然后不断调用 mParent.requestLayout 按深度标记整个子树
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

Invalidate

View体系的渲染 Invalidate

requestLayout相似的, 也是从变更View向上回溯的过程。 区别在于Invalidate回溯时,会 给当前View打上PFLAG_INVALIDATEDPFLAG_DIRTY标签, 并使绘制的缓存⽆效化, 同时会从 View到mParent不断校正脏区的实际位置区间。

void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
       

        // note jin:  如果View不可⻅, 则不需要处理
        if (skipInvalidate()) {
            return;
        }

        ……

            // note jin:  添加DIRTY标识
            mPrivateFlags |= PFLAG_DIRTY;

            if (invalidateCache) {
                // note jin:  添加INVALIDATE标识, 并去掉CACHE有效标识
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }

            //// note jin: 将损坏矩形传播到父视图。
            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
                // note jin:
                p.invalidateChild(this, damage);
            }

            ……
        }
    }

ViewRoot.requestLayout && invalidateRectOnScreen

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


    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            // note jin:
            scheduleTraversals();
        }
    }


    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // note jin: 发送一个同步屏障,优先执行屏幕刷新
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // note jin: 设置一个回调,发送一个异步的 message,此消息因为屏障会优先执行,刷新屏幕
            // note jin: CALLBACK_TRAVERSAL
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

我们发现, ⽆论是requestLayout还是Invalidate, 最终的结果都是scheduleTravels; ⽽ scheduleTravels的实现可以理解为3点:

  • postCallback -> mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token), 将回调加⼊到Choregrapher的队列中, 等待Vsync通知执⾏

  • scheduleVsyncLocked: 申请下⼀帧Vsync信号

  • notifyRendererOfFramePending: 告诉RenderThread⻢上有⼀帧要来了

performTravels

当在FrameDisplayEventReceiver接收到Vsync信号后, 第⼀步就是通过Choreographer中的 Handler发送事件, 进⽽调⽤doFrame。

而在Choreographer中存储不同类型的Callback, 管理的⽅式是链表结构的队列。

    private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;

        @UnsupportedAppUsage
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }
    }

其中action即为真正的执⾏者, 这⾥是⼀个Runnable. 根据下图可以得知, Vsync最终就是执 ⾏了该action,对应ViewRootImpl 中的 TraversalRunnable, 其run会执行到 ViewRootImpl#doTraversal

View体系的渲染 performTraversals

draw的执行需要条件:

  1. Visible
  2. !dirty.isEmpty()
  3. FLAG_INVALIDATE标识(硬件渲染根据该标识进⾏判断reCreateDisplayList)

软件渲染

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

        ……

            // note jin: 申请一块 GraficBuffer
            canvas = mSurface.lockCanvas(dirty);

       ……
       ……
            // note jin: 调用 View 的 draw,从DecorView向下遍历更新
            mView.draw(canvas);

            drawAccessibilityFocusedDrawableIfNeeded(canvas);
        } finally {
            try {
                // note jin: step3: 通过queueBuffer填充到缓冲区
                surface.unlockCanvasAndPost(canvas);
            } ……
        }
        return true;
    }

硬件渲染

    void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
        choreographer.mFrameInfo.markDrawStart();

        // note jin: 硬件更新
        updateRootDisplayList(view, callbacks);

        ……

        // note jin: 通知 RenderThread 渲染
        int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
        ……
    }