本文内代码基于API 31
视图的渲染流程
一、创建视图对象,组建视图树
在Android系统中所有的视图都对应一个view对象,其中包含了视图的尺寸、位置、如何绘制等信息,可以通过 开发人员选项--绘图--显示布局边界 可以查看布局尺寸和位置:
视图对象之间相互影响、相互依赖的关系被以树的形式记录下来,视图树的根节点一般为此window内的DecorView。
二、生成视图树对应的渲染树
每个需要被渲染的视图都要经过至少一次的测量、定位过程才能进行绘制。
2.1. 测量:
父视图调用子视图的measure
方法,传入当前父视图分配给子视图的宽、高以及对应的测量模式:public final void measure(int widthMeasureSpec, int heightMeasureSpec)
。widthMeasureSpec或heightMeasureSpec的高2位表示测量模式,低位表示具体大小,调用 MeasureSpec.getMode
方法获取测量模式,调用MeasureSpec.getSize
方法获取具体分配大小。
测量模式一共有3种:
MeasureSpec.EXACTLY
:父视图已经确定了子视图的确切尺寸;当子视图的LayoutParams
内的with
或height
设置为LayoutParams.MATCH_PARENT
或者>=0的具体数字时,父视图会以此模式调用子视图测量。MeasureSpec.AT_MOST
:父视图返回剩余可用空间给子视图,由子视图决定尺寸;此时子视图的LayoutParams
内的with
或height
设置为LayoutParams.WRAP_CONTENT
。MeasureSpec.UNSPECIFIED
:父视图对子视图没有任何约束,Api23以前子视图得到的分配大小永远为0,版本号23及以后会得到父视图内剩余可用空间;此模式一般在一些特殊情况,如ScrollView
测量子视图时才会用到。
measure方法
scss
//使用宽度值与高度值生成缓存key
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
//创建缓存大小为2的缓存队列
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
//检查强制更新标识,此标识一般在调用requestLayout时添加
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
//测量模式是否变化
final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
|| heightMeasureSpec != mOldHeightMeasureSpec;
//长、宽是否都为精确模式
final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
//长、宽数值是否变化
final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
//sAlwaysRemeasureExactly = targetSdkVersion <= Build.VERSION_CODES.M;
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
//检查更新标识,如果不需要更新跳过这段代码,仅更新缓存
if (forceLayout || needsLayout) {
//清除已测量标识
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
//检查并设置左、右手模式相关参数
resolveRtlPropertiesIfNeeded();
//强制更新不可使用缓存
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
//sIgnoreMeasureCache = targetSdkVersion < Build.VERSION_CODES.KITKAT;
if (cacheIndex < 0 || sIgnoreMeasureCache) {
//无可用缓存或忽略缓存时,执行onMeasure测量自身尺寸
onMeasure(widthMeasureSpec, heightMeasureSpec);
//去除执行layout前进行测量的标识
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
//与缓存key的创建规则类似,高32位记录宽度,低32位记录高度
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
//添加layout前进行测量的标识,由于使用了缓存跳过了onMeasure过程所以要在执行layout方法前执行onMeasure方法,
//猜测这么设计是为了减少onMeasure执行,同时又避免onMeasure一次都不执行
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
//只有调用了setMeasuredDimensionRaw方法后才会重新设置此标识
//避免复写onMeasure后没有设定尺寸
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("View with id " + getId() + ": "
+ getClass().getName() + "#onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
//标记需要重新定位
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
//记录本次测量参数
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
//更新缓存
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL);
具体的测量逻辑写在onMeasure
中,默认的实现仅仅是将父视图传递的尺寸参数设定给自己,需要复写此方法实现其他测量逻辑。
2.2. 定位
仍然是由父视图来调用子视图的layout
方法:
ini
public void layout(int l, int t, int r, int b) {
//根据measure时设定的标识决定是否需要先执行onMeasure
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
//使用局部变量记录旧的边界坐标
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//设定新的边界坐标,特殊布局方式通过setOpticalFrame处理后执行setFrame,默认直接执行setFrame
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
//如果边界坐标有改变,或measure方法中设定了要求重新定位的标识PFLAG_LAYOUT_REQUIRED,执行onLayout,通知位置变化等
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
//默认为空实现,需要复写此方法实现自定义的定位处理
onLayout(changed, l, t, r, b);
//设置滚动条
if (shouldDrawRoundScrollbar()) {
if(mRoundScrollbarRenderer == null) {
mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
}
} else {
mRoundScrollbarRenderer = null;
}
//删除要求重新定位的标识
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
//分发布局定位变化的通知
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
//余下的逻辑大部分是处理焦点的
·······
layout
方法内部较measure
方法简单很多,主要是通过setFrame
方法设置视图的左、上、右、下4个方位坐标,然后通过onLayout
方法实现其他布局定位操作。setFrame
方法除了设定方位坐标,在任何一个边界坐标变化时,通知尺寸变化触发onSizeChanged
,并设定重绘区域。
2.3. 绘制
一般情况下父视图会调用子视图的draw
方法,在源码中的注释里可以看到绘制的大致流程:
markdown
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
* 在遍历视图进行绘制的过程中,以下几个步骤必须以适当的顺序执行
* 1. Draw the background
* 绘制背景
* 2. If necessary, save the canvas' layers to prepare for fading
* 如果有必要,保存画布为绘制渐变做好准备
* 3. Draw view's content
* 绘制视图内容
* 4. Draw children
* 绘制子视图
* 5. If necessary, draw the fading edges and restore layers
* 如果有必要,绘制渐变边框然后绘制画布
* 6. Draw decorations (scrollbars for instance)
* 绘制装饰(例如滚动条)
* 7. If necessary, draw the default focus highlight
* 如果有必要,绘制默认的焦点高亮样式
*/
先看常规情况,不需要步骤2和步骤5
scss
//用于绘制渐变,这个变量在此处只做了声明,没有使用
int saveCount;
//绘制背景
drawBackground(canvas);
//判断是否需要绘制水平或竖直方向的边缘渐变
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
//绘制自身内容
onDraw(canvas);
//遍历绘制子视图
dispatchDraw(canvas);
//绘制获取焦点时的高亮样式
drawAutofilledHighlight(canvas);
//覆盖层,位于内容与前景之间,内部保存多个drawable用于绘制;ViewGroup内的`ViewGroupOverlay`还可以添加view,但是由于不在视图树上,不能触发触摸、测量或绘制事件,需要开发者自己处理。
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
//绘制装饰部分(前景, 滚动条等)
onDrawForeground(canvas);
//绘制获取焦点是的高亮样式
drawDefaultFocusHighlight(canvas);
//开发者模式下的显示布局边界
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
//结束...
return;
}
渐变边框的作用与滚动条类似,用于提示还有内容可以滚动。
ini
/*
* 这里我们完整的流程...
* (这是一种不常见的情况,这也是为什么我们要重复做一些之前做过的测试)
*/
//省略声明变量...
//计算上下左右4个方位的渐变狂位置
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
//ScrollabilityCache内保存了视图滚动时使用的各种字段,避免了在视图对象内保存过多无用的字段,大多数情况下保留太多未使用的字段
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
//如果顶部和顶部的渐变边框有重叠,裁剪至合适的长度
//重叠的渐变会产生奇怪的效果
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
//同样的,如果有需要也对水平渐变的长度进行裁剪
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
//判断顶部和底部是否需要绘制渐变
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
//判断左侧和右侧是否需要绘制渐变
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
//保存画布状态,准备先绘制其他内容
saveCount = canvas.getSaveCount();
int topSaveCount = -1;
int bottomSaveCount = -1;
int leftSaveCount = -1;
int rightSaveCount = -1;
//获取渐变边框的颜色,如果solidColor为0,采用PorterDuff.Mode.DST_OUT模式绘制,否则使用drawRect绘制
int solidColor = getSolidColor();
if (solidColor == 0) {
if (drawTop) {
topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length);
}
if (drawBottom) {
bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
}
if (drawLeft) {
leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom);
}
if (drawRight) {
rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// Step 3,绘制内容
onDraw(canvas);
// Step 4,绘制子视图
dispatchDraw(canvas);
// Step 5,绘制渐变边框,恢复画布状态
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
//必须按与保存顺序相反的顺序恢复
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(rightSaveCount, p);
} else {
canvas.drawRect(right - length, top, right, bottom, p);
}
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(leftSaveCount, p);
} else {
canvas.drawRect(left, top, left + length, bottom, p);
}
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(bottomSaveCount, p);
} else {
canvas.drawRect(left, bottom - length, right, bottom, p);
}
}
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
if (solidColor == 0) {
canvas.restoreUnclippedLayer(topSaveCount, p);
} else {
canvas.drawRect(left, top, right, top + length, p);
}
}
//恢复到最初状态,完成绘制合并
canvas.restoreToCount(saveCount);
需要注意的是,ViewGroup是通过protected boolean drawChild(Canvas canvas, View child, long drawingTime)
方法调用到子时图内的boolean draw(Canvas canvas, ViewGroup parent, long drawingTime)
方法,最后再由此方法内部调用上面的draw
方法。
scss
......
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((RecordingCanvas) canvas).drawRenderNode(renderNode);
} else {
//无背景等绘制时,忽略自身绘制,快速绘制子视图
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
}
} else if (cache != null) {
//使用缓存绘制
}
......
所以public void draw(Canvas canvas)
不一定会执行。
三、GPU将视图树中需要渲染的内容解码成Graphic Buffer;
3.1. 硬件渲染与软件渲染
两者的核心差别在于软件渲染由CPU将Layer(图层)内容转换为Graphic Buffer,硬件渲染由GPU将Layer内容转换为Graphic Buffer。
3.1.1. 软件渲染:
java
//定义在ViewRootImpl.class中
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// 使用软件渲染绘制.
final Canvas canvas;
// 此时我们可以获取到surfaceInsets在x轴、y轴方向上的偏移量,以及需要重绘制的脏区
// 在移动绘制脏区之前,我们需要先加上偏移量
int dirtyXOffset = xoff;
int dirtyYOffset = yoff;
if (surfaceInsets != null) {
dirtyXOffset += surfaceInsets.left;
dirtyYOffset += surfaceInsets.top;
}
try {
dirty.offset(-dirtyXOffset, -dirtyYOffset);
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
// TODO: Do this in native
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not lock surface", e);
// 不要认为这一定是内存溢出造成的,它也可能是因为一些其它原因,如果是别的原因,那么我们可以毫无理由地杀死东西(或我们自己)。
mLayoutRequested = true; // 要求wm下次给一块新的surface。
return false;
} finally {
dirty.offset(dirtyXOffset, dirtyYOffset); // 恢复原始偏移
}
try {
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
//canvas.drawARGB(255, 255, 0, 0);
}
// 如果位图格式中包含透明度通道,我们需要在绘制之前将其清除,
// 以便子控件将其内容恰当地组合到一块透明背景上。 这将自动地对剪辑/脏区域关联。
// 或
// 如果我们设置了偏移,我们要清空偏移后不可见的区域,以免在空白区域留下垃圾
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
mView.draw(canvas); // 传递canvas,完成view绘制
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
try {
surface.unlockCanvasAndPost(canvas); //完成绘制合成,提交结果
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true; // 要求wm下次给一块新的surface。
//noinspection ReturnInsideFinallyBlock
return false;
}
if (LOCAL_LOGV) {
Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
}
}
return true;
}
3.1.2. 硬件渲染:
ini
// ThreadedRenderer.calss
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart();
updateRootDisplayList(view, callbacks); // 更新重绘区域,构建根RenderNode内的DisplayList
// 注册动画绘制节点,并在获得渲染产物前开始动画,动画执行器通常在第一次绘制前启动
if (attachInfo.mPendingAnimatingRenderNodes != null) {
final int count = attachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
registerAnimatingRenderNode(
attachInfo.mPendingAnimatingRenderNodes.get(i));
}
attachInfo.mPendingAnimatingRenderNodes.clear();
// 我们不再需要这个对象,稍后调用ViewRootImpl#attachRenderNodeAnimator方法会直接传递给我们
attachInfo.mPendingAnimatingRenderNodes = null;
}
final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo();
//将RenderNode树同步到渲染线程,并请求绘制帧。
int syncResult = syncAndDrawFrame(frameInfo);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
Log.w("OpenGLRenderer", "Surface lost, forcing relayout");
// 我们没有得到surface。向窗口请求得到新的surface,但愿它能正确工作。
attachInfo.mViewRootImpl.mForceNextWindowRelayout = true;
attachInfo.mViewRootImpl.requestLayout();
}
if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) {
attachInfo.mViewRootImpl.invalidate();
}
}
3.1.3. 主要差别:
- 兼容性:软件渲染的兼容性和稳定性较好,但是复杂图形的渲染速度(或者计算速度)不如硬件渲染。(Android4. 0从开始默认使用硬件渲染)
- 渲染引擎:软件渲染使用Skia渲染;在8.0以前,开启硬件渲染将直接调用OpenGL/Vulkan引擎,在8.0以后,开启硬件渲染将通过Skia间接调用OpenGL/Vulkan引擎;
- 渲染线程:Android5.0以前软件渲染、硬件渲染都在在UI线程;Android5.0以后软件渲染在UI线程,硬件渲染在单独线程;
- 画布对象:软件渲染使用Canvns.class,硬件渲染使用DisplayListCanvas.class;
- RenderNode:Android5.0以后为硬件渲染引入RenderNode(通常一个RenderNode对应一个View,包含自身及其子View的所有DisplayList)和DisplayList(画布上的绘制操作记录)。
- 在重绘脏区时,软件渲染需要重绘与之重叠的所有视图及对应的全部父视图。硬件渲染时只需要重绘脏区对应的视图及其全部父视图(重建DispalyList),其余重叠的视图仍使用之前的DispalyList。
- 当修改透明度、平移等属性时,硬件渲染只修改对应RenderNode的属性,软件需要重绘。
四、合并后提交渲染;
请求垂直同步信号流程:
arduino
View.requestLayout\View.invalidate//视图显示变化
ViewRootImpl.requestLayout\ViewRootImpl.invalidate//传递到ViewRootImpl内对应方法
ViewRootImpl.scheduleTraversals
Choreographer.postCallback//
Choreographer.scheduleFrameLocked//
Choreographer.scheduleVsyncLocked//
DisplayEventReceiver.scheduleVsync//请求垂直同步信号
app处理垂直同步信号流程:
php
Vsync-app//发起app渲染的垂直同步信号
DisplayEventReceiver.onVsync//垂直同步信号监听回调
Choreographer.doFrame//开始渲染
Choreographer.doCallbacks//执行多个callback,执行多个渲染流程
ViewRootImpl.doTraversal//对应CALLBACK_TRAVERSAL类型callback
ViewRootImpl.performTraversals
如果需要设置或修改对应Surface内容,则执行画布创建或修改流程:
* ViewRootImpl.relayoutWindow//重新测量Surface尺寸
* mWindowSession.relayout
* WindowManagerService.relayoutwindow
* WindowManagerService.createSurfaceControl
* WindowStateAnimator.createSurfaceLocked
* new WindowSurfaceController()
* SurfaceControl.Builder.build//创建java端的SurfaceControl
* SurfaceControl.nativeCreate//创建native端的SurfaceControl,两端SurfaceControl联通
* SurfaceComposeClient::createSurfaceChecked//创建native端的Surface
* SurfaceComposeClient::createSurface
* SurfaceComposeClient::createLayer
* SurfaceFlinger::createLayer//创建native端Surface所需GraphicBuffer
* SurfaceFlingercreateBufferLayer\SurfaceFlingercreateColorLayer//双种创建方式
* BufferQueue::createBufferQueue//向BufferQueue请求GraphicBuffer
* ThreadedRenderer.setSurfaceControl//将SurfaceControl设置给渲染线程
performMeasure//开始遍历视图测量
performLayout//开始遍历视图定位
performDraw//开始遍历视图绘制
ViewRootImpl.reportDrawFinished//汇报绘制完成,修改GraphicBuffer状态
mWindowSession.finishDrawing
WindowManagerService.finishDrawingWindow
WindowState.finishDrawing
WindowStateAnimator.finishDrawingLocked
SurfaceControl.Transaction.merge//提交状态变化
SurfaceControl.nativeMergeTransaction
垂直同步信号完成渲染流程:
arduino
Vsync-sf//发起合并信号
SurfaceFlinger::onMessageReceived//接收消息
SurfaceFlinger::handleMessageRefresh
SurfaceFlinger::preComposition//合成之前检查是否有新的变化,如果有,执行请求下一次VSync信号
SurfaceFlinger::rebuildLayerStacks//检查是否需要重新计算合成区域和先后顺序
SurfaceFlinger::setUpHWComposer//询问Hardware Composer HAL(HWC)当前`GraphicBuffer`内每个图层的合成方式,标记为overla(HWC在线合成)或GLES composition(离线合成)
SurfaceFlinger::doDebugFlashRegions//开发者选项中的"显示Surface刷新"功能
SurfaceFlinger::doComposition//SurfaceFlinger将不支持硬件合成的图层进行GPU合成
SurfaceFlinger::postFramebuffer//将GPU合成后的结果和需要HWC合成结果写入FrameBuffer,交给HWC
HWC最终会调用DRM框架进行送显,当下一次硬件VSync信号发生时交换Framebuffer
postComposition(refreshStartTime);//主要用于调试