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()
- 开启硬件加速。不知从哪个 Android 版本开始,默认就需要开启硬件加速。硬件加速,会通过一个渲染线程来绘制 View,而不是在主线程中进行绘制。限于篇幅原因,本文不详细分析硬件加速的流程,只做简单介绍。
- 请求 Vsync 信号,在 Vsync 信号到来时,执行 traversal。一次 traversal 基本包含 measure、layout、draw 等等流程。
- 向 WMS 发起 add window。 根据 Android V app 冷启动(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();
}
}
}