Android-View的绘制流程分析
文章目录
- Android-View的绘制流程分析
- 二、绘制流程图
- 三、代码分析
- 2、ViewRootImpl#requestLayout()
- 3、ViewRootImpl#scheduleTraversals()
- 4、ViewRootImpl#TraversalRunnable
- 5、ViewRootImpl#doTraversal()
- 6、ViewRootImpl#performTraversals()
- 7、ViewRootImpl#performDraw()
- 8、ViewRootImpl#draw()
- 9、ViewRootImpl#drawSoftware()
- 10、View#draw()
- 11、View#dispatchDraw(Canvas canvas)
- 12、ViewGroup#dispatchDraw(Canvas canvas)
- 13、ViewGroup#drawChild
- 14、View#draw(canvas, this, drawingTime)
- 15、View#draw(Canvas canvas)
- 四、总结
一、概述
android系统中列表的滑动,动画的展示,文字的渲染等都是android中的View不断的绘制形成的,今天我们就来分析下android的绘制流程
我会从android的源码层面来逐步分析调用过程,关键代码点,以及作用
二、绘制流程图
三、代码分析
1、View.requestLayout()
这个方法会进行视图的重新测量、布局、绘制
public void requestLayout() {
//清理measure的缓存信息,这个是测量的信息
//这个信息在view的mesure中被创建,measure完成后会被保存下来
/*
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL);
*/
if (mMeasureCache != null) mMeasureCache.clear();
/*
mAttacbhInfo 这个变量在View出现很多,但是感觉我们自己没怎么用过,这个变量不是public的,一般在ViewRootImpl用的多,
mAttacbhInfo:字面意思是附着的信息,我们知道每个View都是在一个窗口之上,这个里面就有关于窗口的信息
还有其他的信息比如token,viewrootImpl信息等
*/
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
//这个viewRoot是从mAttachInfo 获取的
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
//这个的判断含义是,如果ViewRootImpl正在布局的话,就直接返回了,
//大家有兴趣点开ViewRootImpl 跟踪下逻辑就知道了
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
//正在布局的view
mAttachInfo.mViewRequestingLayout = this;
}
//添加一个flag标记
//这个标记是在layout的标记,在View#layout()中去除
//是一个状态标记位
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
//isLayoutRequested() 最终会通过PFLAG_FORCE_LAYOUT来判断,当前是否在layout
//如果父视图正在布局中,就返回
if (mParent != null && !mParent.isLayoutRequested()) {
//核心代码,调用父视图的布局,View的父视图 只能是ViewGroup
//但是ViewGroup 并没有重新requestLayout方法,说明viewgroup还是会调用view的requestlayout
//这个逻辑就形成了一个层层往上调用的逻辑了
//当到达最顶层ViewRootImpl的时候mParent==null了,就会调用ViewRootImpl的requestLayout
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
2、ViewRootImpl#requestLayout()
@Override
1046 public void requestLayout() {
//没有在布局的话 就进入
1047 if (!mHandlingLayoutInLayoutRequest) {
//检查当前操作的线程是否为主线程,这个就是我们常看到的非主线程异常了,这个checkThread 线程检查在
//这里面很多
/*
void checkThread() {
6891 if (mThread != Thread.currentThread()) {
6892 throw new CalledFromWrongThreadException(
6893 "Only the original thread that created a view hierarchy can touch its views.");
6894 }
6895 }
*/
1048 checkThread();
1049 mLayoutRequested = true;
//核心代码,进入计划遍历过程中
1050 scheduleTraversals();
1051 }
1052 }
3、ViewRootImpl#scheduleTraversals()
void scheduleTraversals() {
1223 if (!mTraversalScheduled) {
1224 mTraversalScheduled = true;
1225 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
/*
mChoreographer 这个在我的其他文章中有介绍,这个就是android的一个时间编排者,主要是对视图渲染,输入,动画处理做时间的统一,这个的时间是由ServiceFlinger控制的,说白了,由硬件来参与了控制
发送的Vsync请求信号,然后间隔一定时间收到Vsync信号,这个逻辑由Choreographer做了封装处理
信号类型CALLBACK_TRAVERSAL
我们接着看mTraversalRunnable的实现
*/
1226 mChoreographer.postCallback(
1227 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
1228 if (!mUnbufferedInputDispatch) {
1229 scheduleConsumeBatchedInput();
1230 }
1231 notifyRendererOfFramePending();
1232 pokeDrawLockIfNeeded();
1233 }
1234 }
4、ViewRootImpl#TraversalRunnable
final class TraversalRunnable implements Runnable {
6336 @Override
6337 public void run() {
6338 doTraversal();
6339 }
6340 }
5、ViewRootImpl#doTraversal()
void doTraversal() {
1246 if (mTraversalScheduled) {
1247 mTraversalScheduled = false;
1248 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
1249
1250 if (mProfile) {
1251 Debug.startMethodTracing("ViewAncestor");
1252 }
1253 //核心方法,进入看
1254 performTraversals();
1255
1256 if (mProfile) {
1257 Debug.stopMethodTracing();
1258 mProfile = false;
1259 }
1260 }
1261 }
6、ViewRootImpl#performTraversals()
//这个方法太长了,无法完全拷贝,我写成了伪代码了
//这个方法是真正的执行分发遍历的核心方法,是ViewRootImpl的核心
//这个方法主要做了三件事
performTraversals()
{
xxxxxx
//执行测量流程
performMeasure()
xxxxx
//执行布局流程
performLayout()
xxx
//执行绘制流程
performDraw()
}
/*
上面的每一个流程最终都会触发View树的对应处理流程
我们先用其中的一个performDraw来分析
*/
7、ViewRootImpl#performDraw()
private void performDraw() {
2598 if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
2599 return;
2600 }
2601
2602 final boolean fullRedrawNeeded = mFullRedrawNeeded;
2603 mFullRedrawNeeded = false;
2604
2605 mIsDrawing = true;
2606 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
2607 try {
//核心方法,进入看
2608 draw(fullRedrawNeeded);
2609 } finally {
2610 mIsDrawing = false;
2611 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
2612 }
2613
2614 // For whatever reason we didn't create a HardwareRenderer, end any
2615 // hardware animations that are now dangling
2616 if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
2617 final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
2618 for (int i = 0; i < count; i++) {
2619 mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
2620 }
2621 mAttachInfo.mPendingAnimatingRenderNodes.clear();
2622 }
8、ViewRootImpl#draw()
public void draw(){
xxxx
.....
xxxxxx
省略代码
2810 if (mAttachInfo.mHardwareRenderer != null &&
2811 !mAttachInfo.mHardwareRenderer.isEnabled() &&
2812 mAttachInfo.mHardwareRenderer.isRequested()) {
2813
2814 try {
2815 mAttachInfo.mHardwareRenderer.initializeIfNeeded(
2816 mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
2817 } catch (OutOfResourcesException e) {
2818 handleOutOfResourcesException(e);
2819 return;
2820 }
2821
2822 mFullRedrawNeeded = true;
2823 scheduleTraversals();
2824 return;
2825 }
2826 //这里是核心代码,这里的参数surface对应的表面,dirty是要绘制的区域
2827 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
2828 return;
2829 }
2830 }
2831 }
2832
2833 if (animating) {
2834 mFullRedrawNeeded = true;
2835 scheduleTraversals();
2836 }
2837 }
9、ViewRootImpl#drawSoftware()
2842 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
2843 boolean scalingRequired, Rect dirty) {
2844
2845 // Draw with software renderer.
2846 final Canvas canvas;
2847 try {
2848 final int left = dirty.left;
2849 final int top = dirty.top;
2850 final int right = dirty.right;
2851 final int bottom = dirty.bottom;
2852 //锁定surface 获得canvas 这个操作大家都熟悉把,这个surface是Ui线程下的一个surface,所有的View共享一个surface,
2853 canvas = mSurface.lockCanvas(dirty);
2854
2855 // The dirty rectangle can be modified by Surface.lockCanvas()
2856 //noinspection ConstantConditions
2857 if (left != dirty.left || top != dirty.top || right != dirty.right
2858 || bottom != dirty.bottom) {
2859 attachInfo.mIgnoreDirtyState = true;
2860 }
2861
2862 // TODO: Do this in native
//设置密度
2863 canvas.setDensity(mDensity);
2864 } catch (Surface.OutOfResourcesException e) {
2865 handleOutOfResourcesException(e);
2866 return false;
2867 } catch (IllegalArgumentException e) {
2868 Log.e(mTag, "Could not lock surface", e);
2869 // Don't assume this is due to out of memory, it could be
2870 // something else, and if it is something else then we could
2871 // kill stuff (or ourself) for no reason.
2872 mLayoutRequested = true; // ask wm for a new surface next time.
2873 return false;
2874 }
2875
2876 try {
2877 if (DEBUG_ORIENTATION || DEBUG_DRAW) {
2878 Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
2879 + canvas.getWidth() + ", h=" + canvas.getHeight());
2880 //canvas.drawARGB(255, 255, 0, 0);
2881 }
2882
2883 // If this bitmap's format includes an alpha channel, we
2884 // need to clear it before drawing so that the child will
2885 // properly re-composite its drawing on a transparent
2886 // background. This automatically respects the clip/dirty region
2887 // or
2888 // If we are applying an offset, we need to clear the area
2889 // where the offset doesn't appear to avoid having garbage
2890 // left in the blank areas.
2891 if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
//做一些颜色的初始化
2892 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
2893 }
2894
2895 dirty.setEmpty();
2896 mIsAnimating = false;
2897 mView.mPrivateFlags |= View.PFLAG_DRAWN;
2898
2899 if (DEBUG_DRAW) {
2900 Context cxt = mView.getContext();
2901 Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
2902 ", metrics=" + cxt.getResources().getDisplayMetrics() +
2903 ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
2904 }
2905 try {
//做一些画布的移动
2906 canvas.translate(-xoff, -yoff);
2907 if (mTranslator != null) {
2908 mTranslator.translateCanvas(canvas);
2909 }
2910 canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
2911 attachInfo.mSetIgnoreDirtyState = false;
2912 //这个是核心方法,这个draw是View类的方法
//这个mView就是Dectorview,继承自FrameLayout,FrameLayout继承自ViewGroup
//所以这个draw 我们就先到ViewGroup寻找,发现ViewGroup没有重写,直接用的view 的draw
//去View查看draw,进入看
2913 mView.draw(canvas);
2914
2915 drawAccessibilityFocusedDrawableIfNeeded(canvas);
2916 } finally {
2917 if (!attachInfo.mSetIgnoreDirtyState) {
2918 // Only clear the flag if it was not set during the mView.draw() call
2919 attachInfo.mIgnoreDirtyState = false;
2920 }
2921 }
2922 } finally {
2923 try {
//绘制完成就post,通知绘制
2924 surface.unlockCanvasAndPost(canvas);
2925 } catch (IllegalArgumentException e) {
2926 Log.e(mTag, "Could not unlock surface", e);
2927 mLayoutRequested = true; // ask wm for a new surface next time.
2928 //noinspection ReturnInsideFinallyBlock
2929 return false;
2930 }
2931
2932 if (LOCAL_LOGV) {
2933 Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
2934 }
2935 }
2936 return true;
2937 }
10、View#draw()
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
我们发现google已经为我们清晰的注释了draw要做的工作了
* 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 保存layers
* 3. Draw view's content 画内容
* 4. Draw children 画子视图,这个是核心,这个会调用ViewGroup中的dispatchDraw
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance) //画滑动bar
* 7. If necessary, draw the default focus highlight
*/
// Step 1, draw the background, if needed
int saveCount;
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
//我们继续看View的dispatchDraw方法
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;
}
11、View#dispatchDraw(Canvas canvas)
protected void dispatchDraw(Canvas canvas) {
//空实现,因为view不负责分发,只有去ViewGroup中找了
}
12、ViewGroup#dispatchDraw(Canvas canvas)
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
//处理布局动画的东西
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < childrenCount; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
final LayoutParams params = child.getLayoutParams();
attachLayoutAnimationParameters(child, params, i, childrenCount);
bindLayoutAnimation(child);
}
}
final LayoutAnimationController controller = mLayoutAnimationController;
if (controller.willOverlap()) {
mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
}
controller.start();
mGroupFlags &= ~FLAG_RUN_ANIMATION;
mGroupFlags &= ~FLAG_ANIMATION_DONE;
if (mAnimationListener != null) {
//布局动画的开始回调
mAnimationListener.onAnimationStart(controller.getAnimation());
}
}
/*
下面的过程主要是对viewgroup中的canvas进行裁切,就是clipRect操作,让对应的View只能在对应的区域绘制,所以先要canvas.save 保存当前的状态,后面来恢复,
我们看到里面有padding,这个padding是只有ViewGroup有的,裁切的返回会减去这个边距
*/
int clipSaveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
}
//下面就是开始正式的遍历子视图,然后遍历绘制
// We will draw our child's animation, let's reset the flag
//清除标志
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
boolean more = false;
final long drawingTime = getDrawingTime();
if (usingRenderNodeProperties) canvas.insertReorderBarrier();
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
int transientIndex = transientCount != 0 ? 0 : -1;
// Only use the preordered list if not HW accelerated, since the HW pipeline will do the
// draw reordering internally
final ArrayList<View> preorderedList = usingRenderNodeProperties
? null : buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
/*
这个方法的核心就是对子view的遍历然后绘制分发
*/
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) {
//调用drawChild 绘制
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);
}
}
while (transientIndex >= 0) {
// there may be additional transient views after the normal views
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
break;
}
}
if (preorderedList != null) preorderedList.clear();
// Draw any disappearing views that have animations
//这个mDisappearingChildren 是只的是 正在因为执行动画 即将消失的View,这些View执行了去除动画,所以view已经移除。不能点击,但是还是要把他们绘制出来的
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// Go backwards -- we may delete as animations finish
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
if (usingRenderNodeProperties) canvas.insertInorderBarrier();
if (isShowingLayoutBounds()) {
onDebugDraw(canvas);
}
if (clipToPadding) {
canvas.restoreToCount(clipSaveCount);
}
// mGroupFlags might have been updated by drawChild()
flags = mGroupFlags;
//因为设置了FLAG_INVALIDATE_REQUIRED 可能重绘
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
invalidate(true);
}
if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
mLayoutAnimationController.isDone() && !more) {
// We want to erase the drawing cache and notify the listener after the
// next frame is drawn because one extra invalidate() is caused by
// drawChild() after the animation is over
mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
final Runnable end = new Runnable() {
@Override
public void run() {
notifyAnimationListener();
}
};
//布局动画完成了就回调
post(end);
}
}
13、ViewGroup#drawChild
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
//调用了View的draw方法,进度看
return child.draw(canvas, this, drawingTime);
}
14、View#draw(canvas, this, drawingTime)
//这个方法是View和View的通用绘制处理
//主要是关于Canvas 的裁剪,移动,动画处理,分发
protected void draw(canvas, this, drawingTime)
{
xxxxx
省略代码
...
//关于动画的处理
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
。。。。
int sx = 0;
int sy = 0;
if (!drawingWithRenderNode) {
//看到这个大家熟悉了吗,这个就是那个处理scroller 需要的回调
computeScroll();
sx = mScrollX;
sy = mScrollY;
}
//一堆关于canvas的处理,比如offsetForScroll。canvas.translate,还有Transformation 动画
if (drawingWithRenderNode) {
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
canvas.translate(-transX, -transY);
//实现动画的移动
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
}
// Deal with alpha if it is or used to be <1
//下面代码是处理alpha 透明度的绘制,有些View是设置了alpha的
if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
if (alpha < 1) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA;
} else {
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA;
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
if (!drawingWithDrawingCache) {
final int multipliedAlpha = (int) (255 * alpha);
if (!onSetAlpha(multipliedAlpha)) {
if (drawingWithRenderNode) {
renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
} else if (layerType == LAYER_TYPE_NONE) {
canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),
multipliedAlpha);
}
} else {
// Alpha is handled by the child directly, clobber the layer's alpha
mPrivateFlags |= PFLAG_ALPHA_SET;
}
}
}
} else if ((mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
onSetAlpha(255);
mPrivateFlags &= ~PFLAG_ALPHA_SET;
}
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((RecordingCanvas) canvas).drawRenderNode(renderNode);
} else {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
//核心代码,会调用ViewGroup中的dispathchDraw 然会形成递归调用,直到最后一个是View为止
dispatchDraw(canvas);
} else {
//调用自己的View的draw
draw(canvas);
}
}
}
...
省略代码
xxxxxx
}
15、View#draw(Canvas canvas)
之前那个View#draw,实际是Viewgroup,会调用dispatchDraw
但是现在这个是一个View 调用自己的dispatchDraw 是空实现
到此为止所有的遍历走完了
之前说的measure ,layout跟这个draw的调用类似,自行分析
四、总结
整个过程包括通知和执行
通知布局实质就是调用View的跟节RootViewImpl,
执行:测量、布局、绘制,核心是通过不断的ViewGroup的遍历,迭代,直到最后的View是一个View,为止
我们可以把这个这个View形象的比喻成一棵树,整个就是View树,根部是RootViewImpl,RootViewImpl上面是DectorView,然后在上面就是由各种ViewGroup和View组成的视图,最上面的叶子是View,比如TextView