一、总流程解析:
1、handleResumeActivity
我们首先从ActivityThread的handleResumeActivity方法开始分析view绘制,至于什么时候调用的handleResumeActivity方法,会在启动activity的流程中调用;
handleResumeActivity方法如下所示,主要就干了两件事情;
注释1处执行performResumeActivity,在此方法中会调用到onResume方法中,所以如果我们在Resume生命周期之前获取布局中子view的宽高,会返回0,因为还没有执行addView流程;
注释2处通过wm执行addView方法,这个decor就是我们上一节介绍的DecorView;这个wm是ViewManager接口类型的,WindowManager接口继承自ViewManager;而WindowManagerImpl实现了WindowManager,所示此addView其实是调用的WindowManagerImpl的addView;
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);//1
if (r == null) {
// We didn't actually resume the activity, so skipping any follow-up actions.
return;
}
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
wm.addView(decor, l); //2
}
}
复制代码
2、WindowManagerImpl的addView如下所示,注释1处显示调用的mGlobal的addView,这个mGlobal是WindowManagerGlobal类型的,并且是通过单例模式创建的,WindowManagerGlobal是个单例类;
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId()); //1
}
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); //2
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
复制代码
3、WindowManagerGlobal的addView如下所示,注释1处创建ViewRootImpl对象,注释2处调用了ViewRootImpl的setView方法,这个view参数仍然是包含我们布局的DecorView;
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display); //1
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView, userId); //2
}
}
}
复制代码
4、在ViewRootImpl的setView方法中会调用requestLayout方法,而requestLayout方法又会调用scheduleTraversals方法,scheduleTraversals又会调用doTraversal,doTraversal又会调用performTraversals方法,这个方法应该会比较熟悉了,在这个方法中才真正开始执行view的测量布局绘制的流程;
5、performTraversals方法特别长,但其实总结起来主要就干了三件事,测量、布局和绘制,如下所示;
注释1处将mView赋值给host,这个mView在ViewRootImpl的setView方法中被赋值为docorview,所以这个host就是包含我们布局的decorView;
注释2处执行的performMeasure代表测量流程;
注释3处的performLayout代表布局流程;
注释4处的performDraw代表绘制流程;
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView; //1
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //2
performLayout(lp, mWidth, mHeight); //3
performDraw(); //4
}
复制代码
二、测量流程measure
1、performMeasure
测量流程是从performMeasure方法开始的,如下所示,注释1表示mView为空则直接返回,这个mView我们在setView方法中赋过值,其实就是包含我们布局的DecorView;DecorView、DecorView的父类FrameLayout、FrameLayout的父类ViewGroup都没有measure方法,因此注释2处的measure方法其实是调用的View的measure方法;
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return; //1
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); //2
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
复制代码
2、View的measure方法
measure方法其实就干了一件事情,调用了onMeasure,DecorView重写了onMeasure方法,因此是调用的DecorView的onMeasure方法;在DecorView的onMeasure方法中又调用了super.onMeasure(widthMeasureSpec, heightMeasureSpec),也就是FrameLayout的onMeasure方法;
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
、、、
onMeasure(widthMeasureSpec, heightMeasureSpec);
、、、
}
复制代码
3、FrameLayout的onMeasure方法
注释1处先求出子view个数;
在注释2处表示循环遍历每一个子view然后执行measureChildWithMargins;
测量完所有子view之后在注释3处设置自身的尺寸;
注释4和注释5表示会对所有设置MatchParent属性的子view重新measure,因为MatchParent属性比较特殊,刚开始并不知道父view的尺寸,所以需要重新测量;
我们继续看一下注释2处是怎么测量子view的,FrameLayout并没有measureChildWithMargins方法,而是在父类ViewGroup中定义的,我们接着看一下ViewGroup的measureChildWithMargins方法;
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount(); //1
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0,heightMeasureSpec,0); //2
}
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT)); //3
count = mMatchParentChildren.size(); //4
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
、、、
child.measure(childWidthMeasureSpec, childHeightMeasureSpec); //5
}
}
}
复制代码
4、ViewGroup的measureChildWithMargins方法
注释1处先求出子view的LayoutParams布局参数;
然后在注释2和注释3处通过父view的MeasureSpec和子view的布局参数确定子view的MeasureSpec;
最后在注释4处又调用了child.measure(childWidthMeasureSpec, childHeightMeasureSpec);这样又会递归调用到View的measure方法,在measure方法中又会调用onMeasure方法,如果这个子view是一个viewgroup类型的,则又会递归调用到该viewgroup的onMeasure方法;直到递归调用到最下层view时,则会调用到View的onMeasure方法;
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //1
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width); //2
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height); //3
child.measure(childWidthMeasureSpec, childHeightMeasureSpec); //4
}
复制代码
5、View的onMeasure方法
注释1处的setMeasuredDimension方法设置自身的尺寸,通过getDefaultSize获取尺寸;
注释3和注释4将mMeasuredWidth和mMeasuredHeight赋值为我们测量后的值;因此measure流程结束我们就可以通过getMeasuredWidth和hgetMeasuredeight获取尺寸了,比如我们在onLayout方法中就会可以调用;
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); //1
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
、、、
setMeasuredDimensionRaw(measuredWidth, measuredHeight);//2
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth; //3
mMeasuredHeight = measuredHeight; //4
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
复制代码
6、View的getDefaultSize方法
注释1处获得specMode;
从注释2处我们可知,如果我们自定义view直接继承自View,那么我们不管设置wrap_content还是match_parent效果都是一样的;
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec); //1
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY: //2
result = specSize;
break;
}
return result;
}
复制代码
三、布局流程layout
1、performLayout方法
布局流程的入口函数就是performLayout,如下所示,注释1处将decorView赋值给host,然后在注释2处执行host的layout,也就是DecorView的layout,DecorView、DecorView的父类FrameLayout、FrameLayout的父类ViewGroup都没有重写layout函数,因此实际调用的是View的layout,这块跟measure的逻辑是一样的;
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mInLayout = true;
final View host = mView; //1
if (host == null) {
return;
}
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); //2
mInLayout = false;
}
复制代码
2、View的layout方法
在View的layout方法中其实主要就是调用了onLayout,DecorView重写了onLayout,而在DecorView的onLayout中又调用了super.onLayout(changed, left, top, right, bottom);也就是FrameLayout的onLayout方法;
注释1处的setFrame方法主要是给当前view设置尺寸,具体实现是在注释2、3、4、5处分别给左上右下赋值;所以执行完layout过程后就可以通过getWidth和getHeight获得view的宽高了,我们一般在onDraw方法中调用这两个方法获取view的宽高;
public void layout(int l, int t, int r, int b) {
、、、
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); //1
onLayout(changed, l, t, r, b);
、、、
}
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
// Invalidate our old position
invalidate(sizeChanged);
、、、
mLeft = left; //2
mTop = top; //3
mRight = right; //4
mBottom = bottom; //5
、、、
return changed;
}
复制代码
3、FrameLayout的onLayout方法
注释1处onLayout又调用了layoutChildren;
注释2处计算出所有子view的数量;注释3处循环遍历每一个子view,并执行子view的layout方法;因此又会执行到View的layout方法,进而又会执行到onLayout方法;如果子view又是一个viewgroup,那么还会循环执行上述操作;如果子view是一个具体的view,就是调用到view的onLayout,其实view的onLayout是一个空实现;尺寸的赋值在第2步已经介绍了;
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */); //1
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount(); //2
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;
、、、
child.layout(childLeft, childTop, childLeft + width, childTop + height); //3
}
}
}
复制代码
四、绘制流程draw
1、performDraw
方法的调用链如下边注释1、2、3所示,最终会调用到View的draw方法;
private void performDraw() {
、、、
boolean canUseAsync = draw(fullRedrawNeeded); //1
、、、
}
private boolean draw(boolean fullRedrawNeeded) {
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) { //2
return false;
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
、、、
mView.draw(canvas); //3
、、、
}
复制代码
2、View的draw方法
在view的draw方法中官方也给出了注释,注释中表示draw流程分为7步,在注释中也阐述的非常清楚了,我们不再重复,每一步都可以点进去对应的方法查看,我们主要看一下第三四步;
第3步是通过onDraw方法绘制自身,我们点进去发现是一个空实现,所以需要在具体的view中自己去实现;
第4步通过dispatchDraw(canvas)方法去绘制所有的子view,在View中是一个空实现,在viewgroup中给出了具体的实现;
public void draw(Canvas canvas) {
/*
* 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
*/
// Step 1, draw the background, if needed
drawBackground(canvas);
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
}
复制代码
3、ViewGroup中的dispatchDraw方法
注释1表示在dispatchDraw方法中调用drawChild来绘制子view,注释2表示又会调用每个child的draw方法,这样层层递归最终绘制出整个view;
protected void dispatchDraw(Canvas canvas) {
final int childrenCount = mChildrenCount;
、、、
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); //1
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
}
、、、
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime); //2
}
复制代码
五、注意点:
requestLayout和invalidate以及postInvalidate的区别
1、requestLayout
在view中调用requestLayout后,会递归调用parent的requestLayout,最终会调用到ViewRootImpl的requestLayout方法,如注释1所示,最终调用到performTraversals执行三大流程,因为requestLayout将ForceLayout标志位置为true,因此onMeasure和onLayout肯定会执行,onDraw会根据视图的左上右下参数以及是否是脏数据来决定是否重新执行;
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals(); //1
}
}
复制代码
2、invalidate
invalidate不会设置请求布局的参数,因此onMesaure和onLayout不会执行,而onDraw会执行;
3、postInvalidate
子线程中调用此方法更新视图,实际就是通过handler切换到主线程中,最终还是执行的invalidate;