Android V app 冷启动(10) 真窗绘制

557 阅读13分钟

Activity 会通过 WindowManagerGlobal 添加 DecorView,这会导致 WMS 端创建一个窗口。不同于启动窗口,这个窗口才是 Activity 的真窗。本文就来分析真窗是如何完成绘制的。

ViewRootImpl 保存 View

根据上一篇文章的分析,WindowMangerGlobal 会通过 ViewRootImpl#setView() 保存 DecorView,如下

// ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    setView(view, attrs, panelParentView, UserHandle.myUserId());
}

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
            // 保存 Activity View
            mView = view;

            // ...

            // If the application owns the surface, don't enable hardware acceleration
            if (mSurfaceHolder == null) {
                // 1. 开启硬件加速
                enableHardwareAcceleration(attrs);
                
                // ...
            }

            // ...
            
            // 2. 请求 vsync 信号 ,在 Vsync 信号到来时,执行一次 traversal
            requestLayout();
            
            // ...

            try {
                // ...

                Rect attachedFrame = new Rect();
                final float[] compatScale = { 1f };
                // 3. 向 WMS 发起 add window
                res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), userId,
                        mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets,
                        mTempControls, attachedFrame, compatScale);
                
                // ...
            } catch (RemoteException | RuntimeException e) {
               // ...
            } finally {
               // ...
            }

            // ...
            
            final WindowConfiguration winConfig = getCompatWindowConfiguration();
            // 4.计算窗口 frame
            mWindowLayout.computeFrames(mWindowAttributes, state,
                    displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
                    UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
                    mInsetsController.getRequestedVisibleTypes(), 1f /* compactScale */,
                    mTmpFrames);
            // mWinFrame 保存窗口 frame 
            setFrame(mTmpFrames.frame, true /* withinRelayout */);
            
            // ...

            // DecorView 的 parent 就是 ViewRootImpl
            view.assignParent(this);
            
            // ...
            
            // 在 WMS 端,ActivityRecord 创建之时,默认 client visible 为 true
            // 因此,add window 时,在 res 中会添加 WindowManagerGlobal.ADD_FLAG_APP_VISIBLE
            mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;

           // ...
        }
    }
}

ViewRootImpl#setView()

  1. 开启硬件加速。不知从哪个 Android 版本开始,默认就需要开启硬件加速。硬件加速,会通过一个渲染线程来绘制 View,而不是在主线程中进行绘制。限于篇幅原因,本文不详细分析硬件加速的流程,只做简单介绍。
  2. 请求 Vsync 信号,在 Vsync 信号到来时,执行 traversal。一次 traversal 基本包含 measure、layout、draw 等等流程。
  3. 向 WMS 发起 add window。 根据 Android V app 冷启动(4) 启动窗口的绘制 可知,这里是为真窗建立窗口层级。
  4. 计算窗口 frame。这里主要是根据当前配置,计算一次窗口 frame,为后面的 traversal 做准备。

这里简单的看下窗口 frame 是如何计算的, 如下

// WindowLayout.java

public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
        Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
        int requestedWidth, int requestedHeight, @InsetsType int requestedVisibleTypes,
        float compatScale, ClientWindowFrames frames) {
    final int type = attrs.type;
    final int fl = attrs.flags;
    final int pfl = attrs.privateFlags;
    
    // true
    final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
    
    final Rect attachedWindowFrame = frames.attachedFrame;
    final Rect outDisplayFrame = frames.displayFrame;
    final Rect outParentFrame = frames.parentFrame;
    final Rect outFrame = frames.frame;

    // Activity 真窗是全屏的,不会被 insets 限制的,所以这里 insets 都是 0
    final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(),
            attrs.isFitInsetsIgnoringVisibility());
    final @WindowInsets.Side.InsetsSide int sides = attrs.getFitInsetsSides();
    final int left = (sides & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
    final int top = (sides & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
    final int right = (sides & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
    final int bottom = (sides & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
    
    // display frame 就是 window bounds 大小
    outDisplayFrame.set(windowBounds.left + left, windowBounds.top + top,
            windowBounds.right - right, windowBounds.bottom - bottom);

    if (attachedWindowFrame == null) {
        // parent frame 就是 display frame 大小
        outParentFrame.set(outDisplayFrame);
        if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
            
        }
    } else {
        // ...
    }

    // 关于刘海屏,忽略
    final int cutoutMode = attrs.layoutInDisplayCutoutMode;
    final DisplayCutout cutout = state.getDisplayCutout();
    final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect;
    displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe);
    frames.isParentFrameClippedByDisplayCutout = false;
    if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS && !cutout.isEmpty()) {
        // ...
    }

    // false
    final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
    // false
    final boolean inMultiWindowMode = WindowConfiguration.inMultiWindowMode(windowingMode);


    if (noLimits && type != TYPE_SYSTEM_ERROR && !inMultiWindowMode) {
        
    }

    // false
    final boolean hasCompatScale = compatScale != 1f;
    
    // pw 和 ph 指的是 parent widht 和 parent height
    final int pw = outParentFrame.width();
    final int ph = outParentFrame.height();
    
    // false
    final boolean extendedByCutout =
            (attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0;
    
    // rw 和 rh 指的是 requested width 和 requested height 
    // 目前都是 UNSPECIFIED_LENGTH
    int rw = requestedWidth;
    int rh = requestedHeight;
    
    // x,y 是窗口的偏移坐标
    float x, y;
    
    // w, h 是窗口实际需要的宽高
    int w, h;


    // rw 和 rh 此时都是 UNSPECIFIED_LENGTH,即没有指定请求的宽高
    // 那么只能看看是否能从 attrs.widht 和 attrs.height 中获取
    // 但是它量也没有指定具体数据值,所以 rw 和 rh 只能取自 parent widht 和 parent height
    if (rw == UNSPECIFIED_LENGTH || extendedByCutout) {
        rw = attrs.width >= 0 ? attrs.width : pw;
    }
    if (rh == UNSPECIFIED_LENGTH || extendedByCutout) {
        rh = attrs.height >= 0 ? attrs.height : ph;
    }

    if ((attrs.flags & FLAG_SCALED) != 0) {
       
    } else {
        // attrs.width 和 attrs.height 都是 MATCH_PARENT
        // 因此,窗口实际需要的宽高就是 parent width 和 parent height
        if (attrs.width == MATCH_PARENT) {
            w = pw;
        } else if (hasCompatScale) {
            
        } else {
            
        }
        if (attrs.height == MATCH_PARENT) {
            h = ph;
        } else if (hasCompatScale) {
            
        } else {
            
        }
    }

    if (hasCompatScale) {

    } else {
        // activity 是全屏窗口,并没有指定偏移坐标
        // x,y 都是 0
        x = attrs.x;
        y = attrs.y;
    }

    if (inMultiWindowMode
            && (attrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) == 0) {
        // ...
    }

    
    // true
    final boolean fitToDisplay = !inMultiWindowMode
            || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);

    // 根据窗口的 gravity、窗口实际需要的宽高(w,h)、偏移坐标,计算窗口 frame
    // 但是窗口 frame,必须被限制在 parent frame 中
    // 窗口 frame 会被保存到 outFrame
    Gravity.apply(attrs.gravity, w, h, outParentFrame,
            (int) (x + attrs.horizontalMargin * pw),
            (int) (y + attrs.verticalMargin * ph), outFrame);

    // 窗口 frame 必须限制在 display frame 下
    // 如果超出 display frame,需要移动,甚至裁剪,以达到目的
    // Now make sure the window fits in the overall display frame.
    if (fitToDisplay) {
        Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
    }

    if (extendedByCutout) {

    }

    if (DEBUG) Log.d(TAG, "computeFrames " + attrs.getTitle()
            + " frames=" + frames
            + " windowBounds=" + windowBounds.toShortString()
            + " requestedWidth=" + requestedWidth
            + " requestedHeight=" + requestedHeight
            + " compatScale=" + compatScale
            + " windowingMode=" + WindowConfiguration.windowingModeToString(windowingMode)
            + " displayCutoutSafe=" + displayCutoutSafe
            + " attrs=" + attrs
            + " state=" + state
            + " requestedInvisibleTypes=" + WindowInsets.Type.toString(~requestedVisibleTypes));
}

Activity 是一个全屏窗口,它的 frame 就是 window bounds。对于本系列文章分析的案例来说,它是横屏的大小。至于为何是横屏大小,这与 WMS 发送的配置有关,这里就不详细分析。

traversal

ViewRootImpl#setView() 请求了一个 Vsync 信号,如下

// ViewRootImpl.java

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


void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        
        // 向消息队列 post 一个同步屏障
        // 如果一个同步消息在同步屏障后面 post 的,那么需要等待同步屏障被移除,才能被执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        
        // 向 Choreographer 注册了一个类型为 traversal 的回调 mTraversalRunnable
        // 当 Vsync 信号到来,会执行这个回调
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

当 Vsync 信号到来,会执行回调 mTraversalRunnable,如下

// ViewRootImpl.java
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

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


void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        
        // 移除同步屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        
        // 执行一次 traversal
        performTraversals();
    }
}

一次 traversal 的代码量太大,下面将分部解析。

measure

// ViewRootImpl.java

private void performTraversals() {
    mLastPerformTraversalsSkipDrawReason = null;

    // cache mView since it is used so much below...
    final View host = mView;
    
    // ...


    WindowManager.LayoutParams lp = mWindowAttributes;

    int desiredWindowWidth;
    int desiredWindowHeight;

    // VISIBLE
    final int viewVisibility = getHostVisibility();
    
    // ...
    
    // 在 setView() 中就计算过一次窗口 frame,就是保存到 mWinFrame
    Rect frame = mWinFrame;
    
    
    // mFirst 初始值为 true
    if (mFirst) {
    
        // 需要 draw
        mFullRedrawNeeded = true;
        // 需要 layout
        mLayoutRequested = true;

        final Configuration config = getConfiguration();
        if (shouldUseDisplaySize(lp)) {
            // ...
        } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            // ...
        } else {
            // 注意这段注释,它解释了为何需要在 setView() 先计算一次窗口 frame
            // After addToDisplay, the frame contains the frameHint from window manager, which
            // for most windows is going to be the same size as the result of relayoutWindow.
            // Using this here allows us to avoid remeasuring after relayoutWindow
            
            // 获取 measure 需要的 width 和 height
            desiredWindowWidth = frame.width();
            desiredWindowHeight = frame.height();
        }

        // ...
    } else {
        // ...
    }

    // ...

    // true
    boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
    if (layoutRequested) {
        if (!mFirst) {
            // ...
        }

        // 1. 执行首次 measure
        windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
                desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure);
    }
    
    
    // ...
}


private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
        final Resources res, final int desiredWindowWidth, final int desiredWindowHeight,
        boolean forRootSizeOnly) {
    int childWidthMeasureSpec;
    int childHeightMeasureSpec;
    boolean windowSizeMayChange = false;

    if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
            "Measuring " + host + " in display " + desiredWindowWidth
            + "x" + desiredWindowHeight + "...");

    boolean goodMeasure = false;
    if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
        // ...
    }

    if (!goodMeasure) {
        // 包装成 measure spec
        childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width,
                lp.privateFlags);
        childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
                lp.privateFlags);
        
        // forRootSizeOnly 此时为 false
        if (!forRootSizeOnly || !setMeasuredRootSizeFromSpec(
                childWidthMeasureSpec, childHeightMeasureSpec)) {
            // 执行 measure
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        } else {
            
        }
        
        // 窗口大小发生改变
        if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
            windowSizeMayChange = true;
        }
    }

    if (DBG) {
        // meaure 后的标志性 log
        System.out.println("======================================");
        System.out.println("performTraversals -- after measure");
        host.debug();
    }

    // 返回 true
    return windowSizeMayChange;
}

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    
    // measure trace
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        // 从 DecoreView 开始 measure
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    
    // 保存 measured width 和 measured height
    mMeasuredWidth = mView.getMeasuredWidth();
    mMeasuredHeight = mView.getMeasuredHeight();
    
    mViewMeasureDeferred = false;
}

首次 measure ,就是利用 setView() 计算出的窗口 frame 完成的。有自定义 View 基础的开发者,对这里的代码应该非常熟悉了,我就不再细讲了。

relayout window

// ViewRootImpl.java

private void performTraversals() {
    // ...

    if (layoutRequested) {
        // 1. 执行执行首次 measure
        windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
                desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure);
    }

    // ...

    // 首次 traversal,mFirst 为 true
    // 首次 measure,windowShouldResize 也会为 true
    if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
            || mForceNextWindowRelayout) {
            
        // relayout window trace
        // 这个 trace 数据包含了 relayout window 的原因
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW,
                    TextUtils.formatSimple("%s-relayoutWindow#"
                                    + "first=%b/resize=%b/vis=%b/params=%b/force=%b", mTag,
                            mFirst, windowShouldResize, viewVisibilityChanged, params != null,
                            mForceNextWindowRelayout));
        }
        
        // ...
        
        boolean hadSurface = mSurface.isValid();

        try {
            
            // 2. relayout window
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            
            // ...

            

            // ...
        } catch (RemoteException e) {
        } finally {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }

        // ...
    } else {
        
    }

    // ...
}

// ViewRootImpl.java

// mRelayoutResult 保存了 WSM relayout window 返回的数据
private final WindowRelayoutResult mRelayoutResult = windowSessionRelayoutInfo()
        ? new WindowRelayoutResult(mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
                mTempInsets, mTempControls)
        : null;

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {
    final WindowConfiguration winConfigFromAm = getConfiguration().windowConfiguration;
    final WindowConfiguration winConfigFromWm =
            mLastReportedMergedConfiguration.getGlobalConfiguration().windowConfiguration;
    final WindowConfiguration winConfig = getCompatWindowConfiguration();
    
    // 这是首次 measure 后得到的 measured width 和 measured height
    final int measuredWidth = mMeasuredWidth;
    final int measuredHeight = mMeasuredHeight;
    
    final boolean relayoutAsync;
    if ((mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0
            && mWindowAttributes.type != TYPE_APPLICATION_STARTING
            && mSyncSeqId <= mLastSyncSeqId
            && winConfigFromAm.diff(winConfigFromWm, false /* compareUndefined */) == 0) {
        // ...
    } else {
        relayoutAsync = false;
    }

    // ...

    // requestedWidth 和 requestedHeight 是 relayout window 时请求的窗口宽高
    // appScale 为 1,requestedWidth 和 requestedHeight 就等于 measured width 和 measured height
    final int requestedWidth = (int) (measuredWidth * appScale + 0.5f);
    final int requestedHeight = (int) (measuredHeight * appScale + 0.5f);
    
    int relayoutResult = 0;
    mRelayoutSeq++;
    if (relayoutAsync) {
        // ...
    } else {
        if (windowSessionRelayoutInfo()) {
            // 向 WMS 发起 relayout window
            // requestedWidth 和 requestedHeight 是 measured width 和 measured height
            // viewVisibility 为 VISIBLE
            // mRelayoutResult 保存了 WMS 返回的数据
            relayoutResult = mWindowSession.relayout(mWindow, params,
                    requestedWidth, requestedHeight, viewVisibility,
                    insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                    mRelayoutSeq, mLastSyncSeqId, mRelayoutResult);
        } else {
            // ..
        }
        
        // 代表已经发起过 relayout window
        mRelayoutRequested = true;

        // ...

        // mWinFrameInScreen 保存 relayout window 后得到的窗口 frame
        mWinFrameInScreen.set(mTmpFrames.frame);
        
        // ...
    }

    // ...

    // 计算 surface size,保存到 mSurfaceSize
    // 这个 surface size 包含窗口 frame 大小,以及 surface insets 大小
    WindowLayout.computeSurfaceSize(mWindowAttributes, winConfig.getMaxBounds(), requestedWidth,
            requestedHeight, mWinFrameInScreen, mPendingDragResizing, mSurfaceSize);

    // ...

    if (mSurfaceControl.isValid()) {
    
        // 创建 BLASTBufferQueue,并从其获取绘制的 Surface
        updateBlastSurfaceIfNeeded();
        
        // 硬件渲染,关联窗口的 SurfaceControl
        if (mAttachInfo.mThreadedRenderer != null) {
            mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl, mBlastBufferQueue);
        }
        
        // ...
    } else {
        // ..
    }

    // ...

    // mWinFrame 保存窗口 frame
    // mTmpFrames.frame 是 relayout window 时,WMS 返回的窗口 frame
    setFrame(mTmpFrames.frame, true /* withinRelayout */);
    return relayoutResult;
}

首次 measure 得到的 measured width 和 measured height,被 relayout window 所使用。 根据 Android V app 冷启动(4) 启动窗口的绘制 的分析,WMS relayout window 会创建 surface(实际类型为 SurfaceControl),并计算窗口 frame,并把这些数据返回给 app 端(ViewRootImpl)。

ViewRootImpl 得到返回的数据后,就需要创建 BLASTBufferQueue,如下

// ViewRootImpl.java

void updateBlastSurfaceIfNeeded() {
    // ...
    
    // 创建 BLASTBufferQueue
    mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
            mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
    mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
    Surface blastSurface;
    if (addSchandleToVriSurface()) {
        blastSurface = mBlastBufferQueue.createSurfaceWithHandle();
    } else {
        // 从 BLASTBufferQueue 中获取用于绘制的 Surface
        blastSurface = mBlastBufferQueue.createSurface();
    }
    
    // mSurface 保存 BLASTBufferQueue 创建的 Surface
    mSurface.transferFrom(blastSurface);
}

BLASTBufferQueue 是一个生产消费者模式,它创建的 Surface 就是一个生产者,硬件加速渲染会利用这个 Surface 获取一个 buffer,然后把 View 渲染到这个 buffer 中,最后通知消费者端(SurfaceFlinger)显示这个 surface。本文不深入分析 BLASTBufferQueue,目前只需要知道 BLASTBufferQueue 创建的 Surface 是用于 View 的绘制即可。

整个 relayout window 完成后,traversal 还有后续操作,代码如下,大家简单看下即可

// ViewRootImpl.java

private void performTraversals() {
    // ...

    if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
            || mForceNextWindowRelayout) {
        // ...
        
        boolean hadSurface = mSurface.isValid();

        try {
            
            // 1. relayout window
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            
            // ...

            // relayout window 会得到新的配置,保存到 mPendingMergedConfiguration
            // 首次 traversal,mPendingMergedConfiguration 与 mLastReportedMergedConfiguration 肯定不相等
            // 但是这里仅仅只是保存新配置而已,并不会触发 Activity 配置更新
            // 因为 Activity 的 override config 并没有改变
            boolean shouldPerformConfigurationUpdate =
                    !mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)
                            || !Objects.equals(mPendingActivityWindowInfo,
                            mLastReportedActivityWindowInfo);
            if (mRelayoutRequested && shouldPerformConfigurationUpdate) {
                if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
                        + mPendingMergedConfiguration.getMergedConfiguration());
                performConfigurationChange(new MergedConfiguration(mPendingMergedConfiguration),
                        !mFirst, INVALID_DISPLAY /* same display */,
                        mPendingActivityWindowInfo != null
                                ? new ActivityWindowInfo(mPendingActivityWindowInfo)
                                : null);
                updatedConfiguration = true;
            }
            final boolean updateSurfaceNeeded = mUpdateSurfaceNeeded;
            mUpdateSurfaceNeeded = false;

            surfaceSizeChanged = false;
            if (!mLastSurfaceSize.equals(mSurfaceSize)) {
                surfaceSizeChanged = true;
                // mLastSurfaceSize 保存最新 surface size
                mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
            }
            

            // true
            surfaceCreated = !hadSurface && mSurface.isValid();
            
            // ...

            if (surfaceCreated) {
                mFullRedrawNeeded = true;
                mPreviousTransparentRegion.setEmpty();

                if (mAttachInfo.mThreadedRenderer != null) {
                    try {
                        // 2. 硬件加速渲染,会让 EGL surface 关联 Android Surface
                        hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
                        if (hwInitialized && (host.mPrivateFlags
                                        & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
                            mAttachInfo.mThreadedRenderer.allocateBuffers();
                        }
                    } catch (OutOfResourcesException e) {
                       // ...
                    }
                }
            } 

            // ...
        } catch (RemoteException e) {
        } finally {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }

        // 注意,变量 frame 就是 mWinFrame
        // relayout window 后,使用 mWinFrame 保存了 WMS 返回的窗口 frame
        if (DEBUG_ORIENTATION) Log.v(
                TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);

        mAttachInfo.mWindowLeft = frame.left;
        mAttachInfo.mWindowTop = frame.top;

        // mWidth 和 mHeight 保存 relayout window 返回的窗口宽高
        if (mWidth != frame.width() || mHeight != frame.height()) {
            mWidth = frame.width();
            mHeight = frame.height();
        }

        // ...

        final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer;
        if (threadedRenderer != null && threadedRenderer.isEnabled()) {
            if (hwInitialized
                    || mWidth != threadedRenderer.getWidth()
                    || mHeight != threadedRenderer.getHeight()
                    || mNeedsRendererSetup) {
                // 3. 硬件加速渲染初始化用于渲染的宽高
                // 其实是给 RootRenderNode 设置 Rect
                threadedRenderer.setup(mWidth, mHeight, mAttachInfo,
                        mWindowAttributes.surfaceInsets);
                mNeedsRendererSetup = false;
            }
        }


        if (!mStopped || mReportNextDraw) {
            // 配置更新时, updatedConfiguration 被更新为 true
            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()
                    || dispatchApplyInsets || updatedConfiguration) {
                int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,
                        lp.privateFlags);
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,
                        lp.privateFlags);

                if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                        + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                        + " mHeight=" + mHeight
                        + " measuredHeight=" + host.getMeasuredHeight()
                        + " dispatchApplyInsets=" + dispatchApplyInsets);

                // 4. 再次执行 measure
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                int width = host.getMeasuredWidth();
                int height = host.getMeasuredHeight();
                boolean measureAgain = false;

                if (lp.horizontalWeight > 0.0f) {

                }
                if (lp.verticalWeight > 0.0f) {

                }

                if (measureAgain) {

                }

                // 需要 layout
                layoutRequested = true;
            }
        }
    } else {
        
    }

    // ...
}

layout

// ViewRootImpl.java

    private void performTraversals() {
        // ...

        boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
        if (layoutRequested) {
            // 1. 执行首次 measure
            windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
                    desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure);
        }

        // ...

        if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
                || mForceNextWindowRelayout) {
            // ...

            try {
                
                // 2. relayout window
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                
                // ...
            } 

            // ...
        } else {
            
        } 

        // ...

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            // 3. 执行 layout
            // mWidth 和 mHeight 是 relayout window 时,从 WMS 得到的窗口 frame
            performLayout(lp, mWidth, mHeight);

            // ...
        }

        // ...
    }
// ViewRootImpl.java

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mScrollMayChange = true;
    mInLayout = true;

    final View host = mView;
    if (host == null) {
        return;
    }
    if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
        Log.v(mTag, "Laying out " + host + " to (" +
                host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
    }
    
    // layout trace 
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    
    try {
        // 注意,layout 使用的是 measured width 和 measured height
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

        mInLayout = false;
        int numViewsRequestingLayout = mLayoutRequesters.size();
        if (numViewsRequestingLayout > 0) {
            // ...
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}

layout 流程比较简单,值得注意的是,它使用 measured width 和 measured height 对 DecorView 进行 layout。

draw

// ViewRootImpl.java

private void performTraversals() {
    // ...

    boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
    if (layoutRequested) {
        // 1. 执行首次 measure
        windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
                desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure);
    }

    // ...

    if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
            || mForceNextWindowRelayout) {
        // ...

        try {
            
            // 2. relayout window
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            
            // ...
        } 

        // ...
    } else {
        
    } 

    // ...

    final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
    boolean triggerGlobalLayoutListener = didLayout
            || mAttachInfo.mRecomputeGlobalAttributes;
    if (didLayout) {
        // 3. 执行 layout
        performLayout(lp, mWidth, mHeight);

        // ...
    }

    // 第一次 relayout 后,WMS 会添加 RELAYOUT_RES_FIRST_TIME 标志位
    if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
        // 标记需要向 WMS 上报绘制完成
        reportNextDraw("first_relayout");
    }

    mCheckIfCanDraw = isSyncRequest || cancelDraw;

    // 执行 ViewTreeObserver#addOnPreDrawListener() 注册的 listener
    // 如果有一个 listener 返回 false,代表要取消后面的 draw
    boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
    boolean cancelAndRedraw = cancelDueToPreDrawListener
             || (cancelDraw && mDrewOnceForSync);

    if (!cancelAndRedraw) {
        // ...
        
        // 为 WSM 和 ViewRootImpl 分别创建 SurfaceSyncGroup ,用于同步两端的 surface 绘制
        createSyncIfNeeded();
        
        // ...
    }

    if (!isViewVisible) {
        
    } else if (cancelAndRedraw) {
        
    } else {
        // ...
        
        // 4. 执行 draw
        if (!performDraw(mActiveSurfaceSyncGroup)) {
            // ...
        }
    }
   
    // ...

    if (!cancelAndRedraw) {
        // ...
        
        if (isInWMSRequestedSync()) {
            // 直接标记 WMS 的 SurfaceSyncGroup ready
            mWmsRequestSyncGroup.markSyncReady();
            mWmsRequestSyncGroup = null;
            mWmsRequestSyncGroupState = WMS_SYNC_NONE;
        }
    }

    // ...
}

在正式执行 draw 流程之前,还有一些“小插曲”,暂时可以不用管,直接看下 draw 流程

// ViewRootImpl.java

// surfaceSyncGroup 是 mActiveSurfaceSyncGroup
private boolean performDraw(@Nullable SurfaceSyncGroup surfaceSyncGroup) {
    // ...

    // true
    final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null;
    mFullRedrawNeeded = false;

    mIsDrawing = true;
    
    // draw trace
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw-" + mTag);
    
    // 只有通过 ViewTreeObserver#registerFrameCommitCallback() 
    addFrameCommitCallbackIfNeeded();

    boolean usingAsyncReport;

    try {
        
        // draw
        usingAsyncReport = draw(fullRedrawNeeded, surfaceSyncGroup, mSyncBuffer);
        
        // ...
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }

    // ...

    return true;
}


// fullRedrawNeeded 为 true
// activeSyncGroup 是 mActiveSurfaceSyncGroup
// syncBuffer 为 false
private boolean draw(boolean fullRedrawNeeded, @Nullable SurfaceSyncGroup activeSyncGroup,
        boolean syncBuffer) {
    // ...
    
    final Rect dirty = mDirty;
    
    // ...
    
    // 参数 fullRedrawNeeded 为 true
    if (fullRedrawNeeded) {
        dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
    }    

    boolean useAsyncReport = false;
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
        if (isHardwareEnabled()) {
            // ....

            useAsyncReport = true;

            // ...

            if (activeSyncGroup != null) {
                // 向硬件渲染注册一个 frame callback,在绘制下一帧的时候执行
                registerCallbacksForSync(syncBuffer, activeSyncGroup);
                if (syncBuffer) {
                    // ...
                }
            } else if (mHasPendingTransactions) {
                // ...
            }

            long timeNs = SystemClock.uptimeNanos();
            
            // 使用硬件加速渲染,绘制View
            mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);

            // ...
        } else {
            // ...
        }
    }

    // ...
    
    return useAsyncReport;
}

高 Android 版本,都是默认都是使用硬件加速渲染来绘制 View 的。至于硬件加速渲染如何完成绘制的,本文不做分析。

上报 WMS 绘制完成

draw 流程中,有很多地方没有分析,这其实与 ViewRootImpl 如何上报 WMS finish drawing 有关。接下来看下这是如何实现的。

首先在 draw 之前,需要先标记需要上报 WMS,调用的代码如下

// 第一次 relayout 后,WMS 会添加 RELAYOUT_RES_FIRST_TIME 标志位
    if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
        // 标记需要向 WMS 上报绘制完成
        reportNextDraw("first_relayout");
    }
// ViewRootImpl.java

private void reportNextDraw(String reason) {
    if (DEBUG_BLAST) {
        Log.d(mTag, "reportNextDraw " + Debug.getCallers(5));
    }
    
    // 标记需要上报 WMS finish drawing
    mReportNextDraw = true;
    mLastReportNextDrawReason = reason;
}

然后,需要分别为 WMS 和 ViewRootImpl 创建 SurfaceSyncGroup ,如下

// ViewRootImpl.java

private void createSyncIfNeeded() {
    // WMS requested sync already started or there's nothing needing to sync
    if (isInWMSRequestedSync() || !mReportNextDraw) {
        return;
    }

    final int seqId = mSyncSeqId;
    mWmsRequestSyncGroupState = WMS_SYNC_PENDING;
    
    // 创建 WMS 的 SurfaceSyncGroup
    mWmsRequestSyncGroup = new SurfaceSyncGroup("wmsSync-" + mTag, t -> {
        mWmsRequestSyncGroupState = WMS_SYNC_MERGED;
        if (mWindowSession instanceof Binder) {
            
        } else {
            reportDrawFinished(t, seqId);
        }
    });

    // ...

    if (DEBUG_BLAST) {
        Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName());
    }
    
    // 这里会让 ViewRootImpl 创建 SurfaceSyncGroup,它的名字为 mActiveSurfaceSyncGroup
    // 并作为 child,保存到 WMS 的 SurfaceSyncGroup 中
    mWmsRequestSyncGroup.add(this, null /* runnable */);
}

这两个 SurfaceSyncGroup 是为了同步 WMS 和 ViewRootImpl 的绘制。本系列文章不涉及 WMS 的同步,并且我还没遇到过这种情况,所有暂不做深入分析。

最后,在通过硬件加速渲染绘制 View 之前,向硬件加速渲染注册了一个 frame callback,如下

// ViewRootImpl.java

// syncBuffer 为 false
// surfaceSyncGroup 为 mActiveSurfaceSyncGroup,它是 ViewRootImpl 的 SurfaceSyncGroup
private void  registerCallbacksForSync(boolean syncBuffer,
        final SurfaceSyncGroup surfaceSyncGroup) {
    if (!isHardwareEnabled()) {
        return;
    }

    if (DEBUG_BLAST) {
        Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
    }

    final Transaction t;
    if (mHasPendingTransactions) {
        t = new Transaction();
        t.merge(mPendingTransaction);
    } else {
        t = null;
    }

    // 向硬件加速渲染注册一个 frame callback
    mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
        // ...

        // 硬件加速渲染完成进行回调
        public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
            if (DEBUG_BLAST) {
                Log.d(mTag,
                        "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
                                + frame + ".");
            }
            if (t != null) {
                mergeWithNextTransaction(t, frame);
            }


            if ((syncResult
                    & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
                // ...
            }

            if (DEBUG_BLAST) {
                Log.d(mTag, "Setting up sync and frameCommitCallback");
            }

            if (syncBuffer) {
                // ...
            }

            // 这里返回一个 callback,在硬件加速把渲染 buffer 提交给 SurfaceFlinger 后执行
            return didProduceBuffer -> {
                if (DEBUG_BLAST) {
                    Log.d(mTag, "Received frameCommittedCallback"
                            + " lastAttemptedDrawFrameNum=" + frame
                            + " didProduceBuffer=" + didProduceBuffer);
                }

                if (!didProduceBuffer) {
                    
                }

                if (!syncBuffer) {
                    // 标记 ViewRootImpl 的 SurfaceSyncGroup ready
                    surfaceSyncGroup.markSyncReady();
                }
            };
        }
    });
}

现在,一切就绪,当硬件加速渲染完成,会回调 frame callback,执行 frame callback 还会返回另外一个 callback,这个 callback 是在硬件加速把渲染 buffer 提交给 SurfaceFlinger 后执行,而执行这个 callback 时,会标记 ViewRootImpl 的 SurfaceSyncGroup ready。

在 draw 流程中代码所展示,当把 View 绘制交给硬件加速渲染后,就立即标记 WMS SurfaceSyncGroup ready。而现在它的 child SurfaceSyncGroup,即 ViewRootImpl 的 SurfaceSyncGroup,已经 ready,那么会执行 WMS SurfaceSyncGroup 的回调,去上报 WMS finish drawing,如下

// ViewRootImpl.java

private void createSyncIfNeeded() {
    // WMS requested sync already started or there's nothing needing to sync
    if (isInWMSRequestedSync() || !mReportNextDraw) {
        return;
    }

    final int seqId = mSyncSeqId;
    mWmsRequestSyncGroupState = WMS_SYNC_PENDING;
    mWmsRequestSyncGroup = new SurfaceSyncGroup("wmsSync-" + mTag, 
        // ViewRootImpl 和 WMS SurfaceSyncGroup 都 ready,执行这个回调
        t -> {
            mWmsRequestSyncGroupState = WMS_SYNC_MERGED;
            if (mWindowSession instanceof Binder) {
                // ...
            } else {
                reportDrawFinished(t, seqId);
            }
        }
    );

    // ...

    mWmsRequestSyncGroup.add(this, null /* runnable */);
}


private void reportDrawFinished(@Nullable Transaction t, int seqId) {
    // 这里有 event log 、Android log、systrace
    logAndTrace("reportDrawFinished seqId=" + seqId);
    try {
        // 通过 WMS 完成绘制
        mWindowSession.finishDrawing(mWindow, t, seqId);
    } catch (RemoteException e) {
        Log.e(mTag, "Unable to report draw finished", e);
        if (t != null) {
            t.apply();
        }
    } finally {
        if (t != null) {
            t.clear();
        }
    }
}