View的绘制原理&渲染机制
深入理解Android之View的绘制流程
Android View的绘制流程知识点总结
Android View的渲染过程
关于 Android 渲染你应该了解的知识点
Android屏幕刷新机制原理分析
1.绘制流程从哪里开始
v2-45fd36622d21e7acd47f9abfaec297cf_r.jpg image.png- Activity.setContentView() 其中getWindow()返回的是Activity所关联的PhoneWindow
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
. . .
}
- PhoneWindow.setContentView() ,递归性的解析xml,构建ViewTree,生成DecorView。
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
// mContentParent即为上面提到的ContentView的父容器,若为空则调用installDecor()生成
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 具有FEATURE_CONTENT_TRANSITIONS特性表示开启了Transition
// mContentParent不为null,则移除decorView的所有子View
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 开启了Transition,做相应的处理,我们不讨论这种情况
// 感兴趣的同学可以参考源码
. . .
} else {
// 一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局
// 填充布局也就是把我们设置的ContentView加入到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
. . .
// cb即为该Window所关联的Activity
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
// 调用onContentChanged()回调方法通知Activity窗口内容发生了改变
cb.onContentChanged();
}
. . .
}
- ActivityThread
public final class ActivityThread {
//ApplicationThread中会调用sendMessage(ActivtyThread.H.LAUNCH_ACTIVITY)
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int RESUME_ACTIVITY = 107;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int PAUSE_ACTIVITY = 101;
public static final int DESTROY_ACTIVITY = 109;
public void handleMessage(Message msg) {
switch(msg.what){
case LAUNCH_ACTIVITY :
handleLaunchActivity();
break;
case RESUME_ACTIVITY :
handleResumeActivity();
break;
case STOP_ACTIVITY_SHOW :
handleStopActivity();
break;
case PAUSE_ACTIVITY :
handlePauseActivity();
break;
case DESTROY_ACTIVITY:
handleDestroyActivity();
break;
}
}
}
public Activity handleLaunchActivity() {
final Activity a = performLaunchActivity(r, customIntent);
return a;
}
public Activity performLaunchActivity() {
//attach方法中创建PhoneWindow,设置WindowManager
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
/* onCreate方法setContentView,通过LayoutInflater把布局文件转换成一个java内存中ViewTree
对象,主要的逻辑就是解析递归性的解析xml文件,把每个TAG,用反射的方式来生成一个View对
象,当Xml解析完成后,一颗View Tree就生成完了,但是需要注意的是,inflate之后,ViewTree
是创建好了,但是这仅仅是以单纯对象数据的形式存在,这时去获取View的GUI的相关属性,比如
大小,位置,渲染状态,是不存在的*/
if (r.isPersistable()) {
//>>>activity.performCreate()>>>Activity.onCreate()
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
public void handleResumeActivity(ActivityClientRecord r) {
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
r.window = r.activity.getWindow();
View decor = r.window.getDecorView(); //通过后PhoneWindow获取DecorView
ViewManager wm = a.getWindowManager(); //获取WindowManagerImpl
wm.addView(decor, l);
}
public boolean performResumeActivity(){
//>>>mInstrumentation.callActivityOnResume(this)>>>Activity.onResume()
r.activity.performResume(r.startsNotResumed, reason);
}
public void handlePauseActivity(){
performPauseActivity();
}
private Bundle performPauseActivity(){
//>>>activity.performPause()>>>onPause()
mInstrumentation.callActivityOnPause(r.activity);
}
public void handleStopActivity(){
performStopActivityInner();
}
private void performStopActivityInner(){
// >>>activity.onStop();
mInstrumentation.callActivityOnStop(this);
}
public void handleDestroyActivity(){
performDestroyActivity();
}
void performDestroyActivity(){
//>>>activity.performDestroy()>>>onDestroy()
mInstrumentation.callActivityOnDestroy(r.activity);
}
}
- WindowManagerImpl
public final class WindowManagerImpl implements WindowManager {
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
}
- WindowManagerGlobal
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params) {
ViewRootImpl root;
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession);
}
root.setView(view, wparams, panelParentView, userId);
}
}
- ViewRootImpl (ViewGroup继承View)
(1).ViewRootImpl.performMeasure>>View.measure>>ViewGroup.onMeasure>>ViewGroup.measureChildWithMargins>>View.measure,如果当前view非ViewGroup,View.onMeasure>>View.setMeasuredDimension()测量View自身, 如果view是ViewGroup,ViewGroup会重写父类View.onMeasure()非空方法,会遍历子View,递归性的调用 View.measure()测量子View.
(2).ViewRootImpl.performLayout>>View.layout>>ViewGroup.onLayout>>ViewGroup.layoutChildren>>View.layout,如果当前view非ViewGroup,view.layout方法中会调用View.setFrame(l,t,r,b)定位自身, 如果view是ViewGroup,在ViewGroup会重写父类View.onLayout空方法,遍历子View,递归性的调用View.layout定位每个子View的大小.
(3).ViewRootImpl.pefromDraw>>ViewRootImpl.drawSoftware>>>>View.draw>>View.onDraw,ViewGroup.dispatchDraw>>ViewGroup.drawChild>>View.draw,如果当前view非ViewGroup,View.draw方法中会调用onDraw绘制自身(子View自己实现onDraw)),如果view是ViewGroup,在ViewGroup中会重写父类View中的diapatchDraw空方法,会遍历子View,,递归调用View.draw测量每个子View的大小.
public final class ViewRootImpl {
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//执行mTraversalRunnable接口,调用 doTraversal()
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
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);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
measureHierarchy();
performLayout(lp, mWidth, mHeight); //执行layout
performDraw();//执行draw
}
private void measureHierarchy(){
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //執行測量
}
//传入performMeasure()方法的MeasureSpec的SpecMode为EXACTLY,SpecSize为窗口尺寸。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
try {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
. . .
}
}
}
其中getRootMeasureSpec()方法来获取MeasureSpec,这个根MeasureSpec代表了对DecorView的宽度的约束信息,现在我们来简单介绍一下MeasureSpec的概念。
MeasureSpec是一个32位整数,由SpecMode和SpecSize二部分组成,其中高2位为SpecMode,低30位
为SpecSize,SpecMode为测量模式,SpecSize为相应测量模式下的测量尺寸。View(包括普通View和ViewGroup)的SpecMode由View的LayoutParams结合父View的MeasureSpec生成。
SpecMode的取值可以分为以下三种:
- EXACTLY :父View期望子View的大小是(SpecSize).
- AT_MOST:父View期望子View的大小不得超过SpecSize.
- UNSPECIFIED:父View对子View的大小不作限制,通常用于系统内部.
2.View的整个绘制流程可以分为以下三个阶段:
- measure:判断是否需要重新计算View的大小,如果需要的话就重新计算。
- layout:判断是否需要重新计算View的位置,如果需要的话就重新计算。
-
draw:判断是否需要重新绘制View,如果需要的话就重新绘制
image.png
2.1.measure阶段
如果是普通View(非ViewGroup)来说,只需完成自身的测量工作即可,setMeasureDimension(),具体来说是以getDefaultSize()方法的返回值来作为测量结果。
public class View{
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public final void layout(int widthMeasureSpec, int heightMeasureSpec) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public final void draw(int widthMeasureSpec, int heightMeasureSpec) {
if (!verticalEdges && !horizontalEdges) {
drawBackground(canvas);//绘制背景
onDraw(canvas);//绘制自己
dispatchDraw(canvas);//绘制子View
}
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
// 在View中个空方法,在ViewGroup中是含有abstract关键字的空方法
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
//在View中是个空方法
protected void onDraw(Canvas canvas) {
}
//在View中是个空方法,在ViewGroup中具体实现
protected void dispatchDraw(Canvas canvas) {
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
/*View的getDefaultSize()方法对于AT_MOST和EXACTLY这两种情况都返回了SpecSize作为result。
所以若我们的自定义View直接继承了View类,我们就要自己对wrap_content (对应了AT_MOST)
这种情况进行处理,否则对自定义View指定wrap_content就和match_parent效果一样了。
*/
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
}
如果是ViewGroup,比如DecorView就是FrameLayout,遍历FrameLayout所有子View,逐一通过MeasureChildWithMargins()方法对子View进行测量,取所有子View中,宽度最大和高度最大的子View的宽度和高度作为maxWidth和maxHeight,而后将得到的maxWidth和maxHeight加上padding值,setMeasuredDimension(maxWidth,maxHeight)来确定FrameLayout的大小.
//重写的是View中的onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
. . .
}
容器View通过measureChildWithMargins()方法对所有子View进行测量后,才能得到自身的测量结果。也就是说,对于ViewGroup及其子类来说,要先完成子View的测量,再进行自身的测量(考虑进padding等)
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
//调用子View的measure()方法进行测量
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
getChildMeasureSpec()此方法展现了根据父View的MeasureSpec和子View的LayoutParams生成子View的MeasureSpec的过程。
当父View的SpecMode为EXACTLY时,表示父View对子View指定了确切的宽高限制。
- childDimension>=0:表示子View指定了具体大小,那么子View的SpceMode就是EXACTLY, SpceSize=childDemension。
- childDimension==match_parent :此时表示子View想和父View一样大,那么子View的SpecMode就是EXACTLY,SpecSize=size;
- childDimension==wrap_content:表示子View想自己决定自己的尺寸,那么子View的SpecMode就是AT_MOST,SpecSize=size;
当父View的SpecMode为AT_MOST时,表示父View对子View没有指定确切的宽高限制.
- childDimension>=0:表示子View指定了具体的大小,那么子View的SpecMode 就是EXACTLY,SpecSize=childDemension;
- childDimension==match_parent:表示子View的大小和父View一样大,但是父View还无法确定自身的大小,所以子View的SpceMode就是 AT_MOST,SpecSize=size;
- childDimension==wrap_content:表示子View想自己决定自己的大小,但是父View还无法确定自身的大小,所以子View的SpecMode就是AT_MOST,SpaceSize=size;
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// spec为父View的MeasureSpec
// padding为父View在相应方向的已用尺寸加上父View的padding和子View的margin
// childDimension为子View的LayoutParams的值
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
// 现在size的值为父View相应方向上的可用大小
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
// 表示子View的LayoutParams指定了具体大小值(xx dp)
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {//MATCH_PARENT=-1
// 子View想和父View一样大
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {//WRAP_CONTENT=-2
// 子View想自己决定其尺寸,但不能比父View大
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// 子View指定了具体大小
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 子View想跟父View一样大,但是父View的大小未固定下来
// 所以指定约束子View不能比父View大
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 子View想要自己决定尺寸,但不能比父View大
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
. . .
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
2.2.Layout阶段(基本思想也是由根View开始,递归地完成整个控件树的布局(layout)工作)
- View.layout() 这个方法会调用setFrame()方法来设置View的mLeft、mTop、mRight和mBottom四个参数,这四个参数描述了View相对其父View的位置(分别赋值为l, t, r, b),在setFrame()方法中会判断View的位置是否发生了改变,若发生了改变,则需要对子View进行重新布局,对子View的局部是通过onLayout()方法实现了。由于普通View( 非ViewGroup)不含子View,所以View类的onLayout()方法为空。因此接下来,我们看看ViewGroup类的onLayout()方法的实现。
public void layout(int l, int t, int r, int b) {
// l为本View左边缘与父View左边缘的距离
// t为本View上边缘与父View上边缘的距离
// r为本View右边缘与父View左边缘的距离
// b为本View下边缘与父View上边缘的距离
. . .
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);//onLayout在View中是个空方法
. . .
}
. . .
}
- ViewGroup.onLayout实际上该方法是abstract,这是因为不同的布局管理器有着不同的布局方式,这里我们还是以decorView,也就是FrameLayout的onLayout为例子,分析ViewGroup的布局过程:
遍历ViewGroup中的所有子View,拿到每个View对应的左上右下的值,然后调用child.layout(l,t,r,b)进行定位设置,如果子View是容器View,则会递归的对其子View进行布局。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
2.3.draw阶段(perfromDraw>>draw>>drawSoftWare>>mView.draw())
- View.draw()实际上,View类的onDraw()方法为空,因为每个View绘制自身的方式都不尽相同,对于decorView来说,由于他是FrameLayout,他是容器View,所以他本身并没有什么需要绘制的。dispatchDraw()方法来绘制子View
//canvas = mSurface.lockCanvas(dirty);//canvas是通过Surface获取的
public void draw(Canvas canvas) {
// 绘制背景,只有dirtyOpaque为false时才进行绘制,下同
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 绘制自身内容
if (!dirtyOpaque) onDraw(canvas);
// 绘制子View
dispatchDraw(canvas);//在View中是个空方法,在ViewGroup中具体实现
// 绘制滚动条等
onDrawForeground(canvas);
}
- ViewGroup.dispatchDraw() 遍历ViewGroup中的所有子View,然后调用View.draw方法绘制自身
@Override
protected void dispatchDraw(Canvas canvas) {
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime); //调用View自身的draw()方法绘制自身.
}
3.View的渲染机制
3.1. 基本概念
- CPU:执行应用层面的measure,layout,draw等绘制操作,绘制完成后交由OpenGL,Skia(底层图像渲染库)
- GPU:OpenGL,Skia库使用GPU处理数据,将数据发送到缓冲区
- 屏幕:由一个个像素组成,以固定频率(1000ms,60次 即16.66ms)从缓冲区里读取数据填充像素点。
3.2. Android渲染机制就是一个生产者和消费者的模型
image.png image.png-
图像生成者 Choreographer ,每隔16.66ms收到SufaceFlinger的垂直同步信号,调用doFrame进行下一帧图形数据准备(Cpu对ViewTree进行pefromMeasure,perfomLayout,performDraw绘制,如果之前测量过,或者layout过,或者draw过,就不会再次递归性调用View.measure,View.layout,View.draw了),通过Surface获取canvas,canvas会把绘制指令传递到FrameWork层的RenderThread线程,RenderThread线程通过Surface.dequeue从BufferQueue中申请一块GraphicBuffer(图形缓冲区),通过OpenGL或者Skia等底层图形渲染库调用GUP进行绘制填充,最后在把缓冲区交还给BufferQueue队列中.
-
消费者surfaceFlinger从队列中获取数据,对数据进行合成(DecorView和系统StatusBar),合成了一帧完整的数据之后,就会和HAL通信,替换下一帧数据,等到收到Vsyn垂直同步信号的时候,下一帧数据就会替换当前帧数据,显示到屏幕上了.
-
注意SurfaceFlinger每隔16.66ms就会给HAL(Hardware Abstraction Layer)和应用层(Choreographer)发送一个Vsyn,Choreographer负责在主线程中完成绘制流程(如果主线程ViewTree发生变化,导致重绘,就会重新走View.measure,View.layout,View.draw 此时如果主线程被阻塞(STW,非主线程耗时操作),可能会导致掉帧。
3.2. Choreograher绘制UI的流程
VIewRootImpl.requestLayout >>VIewRootImpl.scheduleTraversal>> Handler.Looper.MessageQueue.postSyncBarrier在队列中插入入一个同步屏障消息)>>ChoreoGrapher.postCallback(CALLBACK_TRAVERSAL)>>scheduleVsyncLocked(向Native层申请Vsyn)>>nativateScheduleVsync(向SurfaceFlinger注册了一个观察者)>> SurfaceFlinger每隔16.66ms之后,回回调观察者的onSync方法>>onSync>>Handler.sendMessageAtTime>>run>>doFrame>>doTraversal>>performMeasure>>performLayout>>performDraw.
3.3. 屏障消息的处理机制
(1).什么是屏障消息?
屏障消息就是没有Target的消息,不需要回调handleMessage,消息队列中的消息是按照时间顺序排列的,因此屏障消息会被插入到所有在其之前应该被处理的消息之后(队尾).
(2).为什么绘制消息的优先级最高,会被首先处理。
// 向Handler发送一个消息,指定的时间执行,并且是异步消息
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK,
action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
原理:主线程的 Looper 会一直循环调用 MessageQueue 的 next() 来取出队头的 Message 执行,当 Message 执行完后再去取下一个。当 next() 方法在取 Message 时发现队头是一个同步屏障的消息时,就会去遍历整个队列,只寻找设置了异步标志(setAsynChronous(true))的消息,如果有找到异步消息,那么就取出这个异步消息来执行,否则就让 next() 方法陷入阻塞状态。如果 next() 方法陷入阻塞状态,那么主线程此时就是处于空闲状态的,也就是没在干任何事。所以,如果队头是一个同步屏障的消息的话,那么在它后面的所有同步消息就都被拦截住了,直到这个同步屏障消息被移除,否则主线程就一直不会去处理同步屏障后面的同步消息.
image.png 1732099432146.png4.View.invalidate分析
1732444078(1).png1732445053(1).png
4.1.View.invalidate
public class View {
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean fullInvalidate) {
//fullInvalidate是true, mPrivateFlags &= ~PFLAG_DRAWN 打上标记
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect; //把自身的位置传递给父View
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
}
}
4.2.ViewGroup.invalidateChild
public class ViewGroup implement ViewParent{
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
//递归循环调用,最终Parent会变成ViewRootImpl,最终ViewRootImpl.invalidateChildParent
do {
//location是父View的位置int[left,top],dirty是子View的位置(Rect(l,t,r,b))
parent = parent.invalidateChildInParent(location, dirty);
} while (parent != null);
}
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
//子View和父View的大小求交集
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
} else {
//子View和父View的大小求交集
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
location[CHILD_LEFT_INDEX] = mLeft; //当前View距离屏幕左边的距离
location[CHILD_TOP_INDEX] = mTop;//当前View距离屏幕顶部的距离
mPrivateFlags &= ~PFLAG_DRAWN; //打上绘制标记
return parent;//返回父View
}
}
4.3.ViewRootImpl.invalidateChildInParent
public class ViewRootImpl implement ViewParent{
invalidateRectOnScreen(dirty);
}
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
//父View和子View的大小求交集
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
//重新执行 doTraversal>>performMeasure>>performLayout>>performDraw
scheduleTraversals();
}
}