转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/51159419

如何优化你的布局层级结构之RelativeLayout和LinearLayout及FrameLayout性能分析

工作一段时间后,经常会被领导说,你这个进入速度太慢了,竞品的进入速度很快,你搞下优化吧?每当这时,你会怎么办?功能实现都有啊,进入时要加载那么多view,这也没办法啊,等等。

先看一些现象吧:用Android studio,新建一个Activity自动生成的布局文件都是RelativeLayout,或许你会认为这是IDE的默认设置问题,其实不然,这是由 android-sdk\tools\templates\activities\EmptyActivity\root\res\layout\activity_simple.xml.ftl 这个文件事先就定好了的,也就是说这是Google的选择,而非IDE的选择。那SDK为什么会默认给开发者新建一个默认的RelativeLayout布局呢?当然是因为RelativeLayout的性能更优,性能至上嘛。但是我们再看看默认新建的这个RelativeLayout的父容器,也就是当前窗口的顶级View——DecorView,它却是个垂直方向的LinearLayout,上面是标题栏,下面是内容栏。那么问题来了,Google为什么给开发者默认新建了个RelativeLayout,而自己却偷偷用了个LinearLayout,到底谁的性能更高,开发者该怎么选择呢?

View的一些基本工作原理

先通过几个问题,简单的了解写android中View的工作原理吧。

View是什么?

简单来说,View是Android系统在屏幕上的视觉呈现,也就是说你在手机屏幕上看到的东西都是View。

View是怎么绘制出来的?

View的绘制流程是从ViewRoot的performTraversals()方法开始,依次经过measure(),layout()和draw()三个过程才最终将一个View绘制出来。

View是怎么呈现在界面上的?

Android中的视图都是通过Window来呈现的,不管Activity、Dialog还是Toast它们都有一个Window,然后通过WindowManager来管理View。Window和顶级View——DecorView的通信是依赖ViewRoot完成的。

View和ViewGroup什么区别?

不管简单的Button和TextView还是复杂的RelativeLayout和ListView,他们的共同基类都是View。所以说,View是一种界面层控件的抽象,他代表了一个控件。那ViewGroup是什么东西,它可以被翻译成控件组,即一组View。ViewGroup也是继承View,这就意味着View本身可以是单个控件,也可以是多个控件组成的控件组。根据这个理论,Button显然是个View,而RelativeLayout不但是一个View还可以是一个ViewGroup,而ViewGroup内部是可以有子View的,这个子View同样也可能是ViewGroup,以此类推。

RelativeLayout和LinearLayout性能PK

基于以上原理和大背景,我们要探讨的性能问题,说的简单明了一点就是:当RelativeLayout和LinearLayout分别作为ViewGroup,表达相同布局时绘制在屏幕上时谁更快一点。上面已经简单说了View的绘制,从ViewRoot的performTraversals()方法开始依次调用perfromMeasure、performLayout和performDraw这三个方法。这三个方法分别完成顶级View的measure、layout和draw三大流程,其中perfromMeasure会调用measure,measure又会调用onMeasure,在onMeasure方法中则会对所有子元素进行measure,这个时候measure流程就从父容器传递到子元素中了,这样就完成了一次measure过程,接着子元素会重复父容器的measure,如此反复就完成了整个View树的遍历。同理,performLayout和performDraw也分别完成perfromMeasure类似的流程。通过这三大流程,分别遍历整棵View树,就实现了Measure,Layout,Draw这一过程,View就绘制出来了。那么我们就分别来追踪下RelativeLayout和LinearLayout这三大流程的执行耗时。
如下图,我们分别用两用种方式简单的实现布局测试下

LinearLayout

Measure:0.762ms
Layout:0.167ms
draw:7.665ms

RelativeLayout

Measure:2.180ms
Layout:0.156ms
draw:7.694ms
从这个数据来看无论使用RelativeLayout还是LinearLayout,layout和draw的过程两者相差无几,考虑到误差的问题,几乎可以认为两者不分伯仲,关键是Measure的过程RelativeLayout却比LinearLayout慢了一大截。

Measure都干什么了

RelativeLayout的onMeasure()方法
 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mDirtyHierarchy) {mDirtyHierarchy = false;sortChildren();}int myWidth = -1;int myHeight = -1;int width = 0;int height = 0;final int widthMode = MeasureSpec.getMode(widthMeasureSpec);final int heightMode = MeasureSpec.getMode(heightMeasureSpec);final int widthSize = MeasureSpec.getSize(widthMeasureSpec);final int heightSize = MeasureSpec.getSize(heightMeasureSpec);// Record our dimensions if they are known;if (widthMode != MeasureSpec.UNSPECIFIED) {myWidth = widthSize;}if (heightMode != MeasureSpec.UNSPECIFIED) {myHeight = heightSize;}if (widthMode == MeasureSpec.EXACTLY) {width = myWidth;}if (heightMode == MeasureSpec.EXACTLY) {height = myHeight;}View ignore = null;int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;int left = Integer.MAX_VALUE;int top = Integer.MAX_VALUE;int right = Integer.MIN_VALUE;int bottom = Integer.MIN_VALUE;boolean offsetHorizontalAxis = false;boolean offsetVerticalAxis = false;if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {ignore = findViewById(mIgnoreGravity);}final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;// We need to know our size for doing the correct computation of children positioning in RTL// mode but there is no practical way to get it instead of running the code below.// So, instead of running the code twice, we just set the width to a "default display width"// before the computation and then, as a last pass, we will update their real position with// an offset equals to "DEFAULT_WIDTH - width".final int layoutDirection = getLayoutDirection();if (isLayoutRtl() && myWidth == -1) {myWidth = DEFAULT_WIDTH;}View[] views = mSortedHorizontalChildren;int count = views.length;for (int i = 0; i < count; i++) {View child = views[i];if (child.getVisibility() != GONE) {LayoutParams params = (LayoutParams) child.getLayoutParams();int[] rules = params.getRules(layoutDirection);applyHorizontalSizeRules(params, myWidth, rules);measureChildHorizontal(child, params, myWidth, myHeight);if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {offsetHorizontalAxis = true;}}}views = mSortedVerticalChildren;count = views.length;final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;for (int i = 0; i < count; i++) {final View child = views[i];if (child.getVisibility() != GONE) {final LayoutParams params = (LayoutParams) child.getLayoutParams();applyVerticalSizeRules(params, myHeight, child.getBaseline());measureChild(child, params, myWidth, myHeight);if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {offsetVerticalAxis = true;}if (isWrapContentWidth) {if (isLayoutRtl()) {if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {width = Math.max(width, myWidth - params.mLeft);} else {width = Math.max(width, myWidth - params.mLeft - params.leftMargin);}} else {if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {width = Math.max(width, params.mRight);} else {width = Math.max(width, params.mRight + params.rightMargin);}}}if (isWrapContentHeight) {if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {height = Math.max(height, params.mBottom);} else {height = Math.max(height, params.mBottom + params.bottomMargin);}}if (child != ignore || verticalGravity) {left = Math.min(left, params.mLeft - params.leftMargin);top = Math.min(top, params.mTop - params.topMargin);}if (child != ignore || horizontalGravity) {right = Math.max(right, params.mRight + params.rightMargin);bottom = Math.max(bottom, params.mBottom + params.bottomMargin);}}}// Use the top-start-most laid out view as the baseline. RTL offsets are// applied later, so we can use the left-most edge as the starting edge.View baselineView = null;LayoutParams baselineParams = null;for (int i = 0; i < count; i++) {final View child = views[i];if (child.getVisibility() != GONE) {final LayoutParams childParams = (LayoutParams) child.getLayoutParams();if (baselineView == null || baselineParams == null|| compareLayoutPosition(childParams, baselineParams) < 0) {baselineView = child;baselineParams = childParams;}}}mBaselineView = baselineView;if (isWrapContentWidth) {// Width already has left padding in it since it was calculated by looking at// the right of each child viewwidth += mPaddingRight;if (mLayoutParams != null && mLayoutParams.width >= 0) {width = Math.max(width, mLayoutParams.width);}width = Math.max(width, getSuggestedMinimumWidth());width = resolveSize(width, widthMeasureSpec);if (offsetHorizontalAxis) {for (int i = 0; i < count; i++) {final View child = views[i];if (child.getVisibility() != GONE) {final LayoutParams params = (LayoutParams) child.getLayoutParams();final int[] rules = params.getRules(layoutDirection);if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {centerHorizontal(child, params, width);} else if (rules[ALIGN_PARENT_RIGHT] != 0) {final int childWidth = child.getMeasuredWidth();params.mLeft = width - mPaddingRight - childWidth;params.mRight = params.mLeft + childWidth;}}}}}if (isWrapContentHeight) {// Height already has top padding in it since it was calculated by looking at// the bottom of each child viewheight += mPaddingBottom;if (mLayoutParams != null && mLayoutParams.height >= 0) {height = Math.max(height, mLayoutParams.height);}height = Math.max(height, getSuggestedMinimumHeight());height = resolveSize(height, heightMeasureSpec);if (offsetVerticalAxis) {for (int i = 0; i < count; i++) {final View child = views[i];if (child.getVisibility() != GONE) {final LayoutParams params = (LayoutParams) child.getLayoutParams();final int[] rules = params.getRules(layoutDirection);if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {centerVertical(child, params, height);} else if (rules[ALIGN_PARENT_BOTTOM] != 0) {final int childHeight = child.getMeasuredHeight();params.mTop = height - mPaddingBottom - childHeight;params.mBottom = params.mTop + childHeight;}}}}}if (horizontalGravity || verticalGravity) {final Rect selfBounds = mSelfBounds;selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,height - mPaddingBottom);final Rect contentBounds = mContentBounds;Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,layoutDirection);final int horizontalOffset = contentBounds.left - left;final int verticalOffset = contentBounds.top - top;if (horizontalOffset != 0 || verticalOffset != 0) {for (int i = 0; i < count; i++) {final View child = views[i];if (child.getVisibility() != GONE && child != ignore) {final LayoutParams params = (LayoutParams) child.getLayoutParams();if (horizontalGravity) {params.mLeft += horizontalOffset;params.mRight += horizontalOffset;}if (verticalGravity) {params.mTop += verticalOffset;params.mBottom += verticalOffset;}}}}}if (isLayoutRtl()) {final int offsetWidth = myWidth - width;for (int i = 0; i < count; i++) {final View child = views[i];if (child.getVisibility() != GONE) {final LayoutParams params = (LayoutParams) child.getLayoutParams();params.mLeft -= offsetWidth;params.mRight -= offsetWidth;}}}setMeasuredDimension(width, height);}

根据源码我们发现RelativeLayout会对子View做两次measure。这是为什么呢?首先RelativeLayout中子View的排列方式是基于彼此的依赖关系,而这个依赖关系可能和布局中View的顺序并不相同,在确定每个子View的位置的时候,就需要先给所有的子View排序一下。又因为RelativeLayout允许A,B 2个子View,横向上B依赖A,纵向上A依赖B。所以需要横向纵向分别进行一次排序测量。

LinearLayout的onMeasure()方法
  @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mOrientation == VERTICAL) {measureVertical(widthMeasureSpec, heightMeasureSpec);} else {measureHorizontal(widthMeasureSpec, heightMeasureSpec);}}

与RelativeLayout相比LinearLayout的measure就简单明了的多了,先判断线性规则,然后执行对应方向上的测量。随便看一个吧。

for (int i = 0; i < count; ++i) {final View child = getVirtualChildAt(i);if (child == null) {mTotalLength += measureNullChild(i);continue;}if (child.getVisibility() == View.GONE) {i += getChildrenSkipCount(child, i);continue;}if (hasDividerBeforeChildAt(i)) {mTotalLength += mDividerHeight;}LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();totalWeight += lp.weight;if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {// Optimization: don't bother measuring children who are going to use// leftover space. These views will get measured again down below if// there is any leftover space.final int totalLength = mTotalLength;mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);} else {int oldHeight = Integer.MIN_VALUE;if (lp.height == 0 && lp.weight > 0) {// heightMode is either UNSPECIFIED or AT_MOST, and this// child wanted to stretch to fill available space.// Translate that to WRAP_CONTENT so that it does not end up// with a height of 0oldHeight = 0;lp.height = LayoutParams.WRAP_CONTENT;}// Determine how big this child would like to be. If this or// previous children have given a weight, then we allow it to// use all available space (and we will shrink things later// if needed).measureChildBeforeLayout(child, i, widthMeasureSpec, 0, heightMeasureSpec,totalWeight == 0 ? mTotalLength : 0);if (oldHeight != Integer.MIN_VALUE) {lp.height = oldHeight;}final int childHeight = child.getMeasuredHeight();final int totalLength = mTotalLength;mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +lp.bottomMargin + getNextLocationOffset(child));if (useLargestChild) {largestChildHeight = Math.max(childHeight, largestChildHeight);}}

父视图在对子视图进行measure操作的过程中,使用变量mTotalLength保存已经measure过的child所占用的高度,该变量刚开始时是0。在for循环中调用measureChildBeforeLayout()对每一个child进行测量,该函数实际上仅仅是调用了measureChildWithMargins(),在调用该方法时,使用了两个参数。其中一个是heightMeasureSpec,该参数为LinearLayout本身的measureSpec;另一个参数就是mTotalLength,代表该LinearLayout已经被其子视图所占用的高度。 每次for循环对child测量完毕后,调用child.getMeasuredHeight()获取该子视图最终的高度,并将这个高度添加到mTotalLength中。在本步骤中,暂时避开了lp.weight>0的子视图,即暂时先不测量这些子视图,因为后面将把父视图剩余的高度按照weight值的大小平均分配给相应的子视图。源码中使用了一个局部变量totalWeight累计所有子视图的weight值。处理lp.weight>0的情况需要注意,如果变量heightMode是EXACTLY,那么,当其他子视图占满父视图的高度后,weight>0的子视图可能分配不到布局空间,从而不被显示,只有当heightMode是AT_MOST或者UNSPECIFIED时,weight>0的视图才能优先获得布局高度。最后我们的结论是:如果不使用weight属性,LinearLayout会在当前方向上进行一次measure的过程,如果使用weight属性,LinearLayout会避开设置过weight属性的view做第一次measure,完了再对设置过weight属性的view做第二次measure。由此可见,weight属性对性能是有影响的,而且本身有大坑,请注意避让。

本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/51159419

小结

从源码中我们似乎能看出,我们先前的测试结果中RelativeLayout不如LinearLayout快的根本原因是RelativeLayout需要对其子View进行两次measure过程。而LinearLayout则只需一次measure过程,所以显然会快于RelativeLayout,但是如果LinearLayout中有weight属性,则也需要进行两次measure,但即便如此,应该仍然会比RelativeLayout的情况好一点。

RelativeLayout另一个性能问题

对比到这里就结束了嘛?显然没有!我们再看看View的Measure()方法都干了些什么?

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||widthMeasureSpec != mOldWidthMeasureSpec ||heightMeasureSpec != mOldHeightMeasureSpec) {......}mOldWidthMeasureSpec = widthMeasureSpec;mOldHeightMeasureSpec = heightMeasureSpec;mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |(long) mMeasuredHeight & 0xffffffffL); // suppress sign extension}

View的measure方法里对绘制过程做了一个优化,如果我们或者我们的子View没有要求强制刷新,而父View给子View的传入值也没有变化(也就是说子View的位置没变化),就不会做无谓的measure。但是上面已经说了RelativeLayout要做两次measure,而在做横向的测量时,纵向的测量结果尚未完成,只好暂时使用myHeight传入子View系统,假如子View的Height不等于(设置了margin)myHeight的高度,那么measure中上面代码所做得优化将不起作用,这一过程将进一步影响RelativeLayout的绘制性能。而LinearLayout则无这方面的担忧。解决这个问题也很好办,如果可以,尽量使用padding代替margin。

FrameLayout和LinearLayout性能PK

FrameLayout
LinearLayout

Measure:2.058ms
Layout:0.296ms
draw:3.857ms

FrameLayout

Measure:1.334ms
Layout:0.213ms
draw:3.680ms
从这个数据来使用LinearLayout,仅嵌套一个LinearLayou,在onMeasure就相关2倍时间和FrameLayout相比,layout和draw的过程两者相差无几,考虑到误差的问题,几乎可以认为两者不分伯仲

看下FrameLayout的源码,做了什么?

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int count = getChildCount();final boolean measureMatchParentChildren =MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;//当FrameLayout的宽和高,只有同时设置为match_parent或者指定的size,那么这个//measureMatchParentChlidren = false,否则为true。下面会用到这个变量mMatchParentChildren.clear();int maxHeight = 0;     int maxWidth = 0;int childState = 0;    //宽高的期望类型for (int i = 0; i < count; i++) {    //一次遍历每一个不为GONE的子viewfinal View child = getChildAt(i);    if (mMeasureAllChildren || child.getVisibility() != GONE) {//去掉FrameLayout的左右padding,子view的左右margin,这时候,再去//计算子view的期望的值measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);final LayoutParams lp = (LayoutParams) child.getLayoutParams();/*maxWidth找到子View中最大的宽,高同理,为什么要找到他,因为在这里,FrameLayout是wrap-content.他的宽高肯定受子view的影响*/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());/*下面的判断,只有上面的FragLayout的width和height都设置为match_parent 才不会执行此处的mMatchParentChlidren的list里存的是设置为match_parent的子view。结合上面两句话的意思,当FrameLayout设置为wrap_content,这时候要把所有宽高设置为match_parent的子View都记录下来,记录下来干什么呢?这时候FrameLayout的宽高同时受子View的影响*/if (measureMatchParentChildren) {if (lp.width == LayoutParams.MATCH_PARENT ||lp.height == LayoutParams.MATCH_PARENT) {mMatchParentChildren.add(child);}}}}// Account for padding toomaxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();// Check against our minimum height and widthmaxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());// Check against our foreground's minimum height and widthfinal 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));count = mMatchParentChildren.size();//这个大小就是子view中设定为match_parent的个数if (count > 1) {for (int i = 0; i < count; i++) {//这里看上去重新计算了一遍final View child = mMatchParentChildren.get(i);final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidthMeasureSpec;int childHeightMeasureSpec;/*如果子view的宽是match_parent,则宽度期望值是总宽度-padding-margin如果子view的宽是指定的比如100dp,则宽度期望值是padding+margin+width这个很容易理解,下面的高同理*/if (lp.width == LayoutParams.MATCH_PARENT) {childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -getPaddingLeftWithForeground() - getPaddingRightWithForeground() -lp.leftMargin - lp.rightMargin,MeasureSpec.EXACTLY);} else {childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,getPaddingLeftWithForeground() + getPaddingRightWithForeground() +lp.leftMargin + lp.rightMargin,lp.width);}if (lp.height == LayoutParams.MATCH_PARENT) {childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -getPaddingTopWithForeground() - getPaddingBottomWithForeground() -lp.topMargin - lp.bottomMargin,MeasureSpec.EXACTLY);} else {childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,getPaddingTopWithForeground() + getPaddingBottomWithForeground() +lp.topMargin + lp.bottomMargin,lp.height);}//把这部分子view重新计算大小child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}}}

本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/51159419
加了一个嵌套,onMeasure时间,多了将近一倍,原因在于:LinearLayout在某一方向onMeasure,发现还存在LinearLayout。将触发

 if (useLargestChild && (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {mTotalLength = 0;for (int i = 0; i < count; ++i) {final View child = getVirtualChildAt(i);if (child == null) {mTotalLength += measureNullChild(i);continue;}if (child.getVisibility() == GONE) {i += getChildrenSkipCount(child, i);continue;}
}

因为二级LinearLayout父类是Match_parent,所以就存在再层遍历。在时间就自然存在消耗。

结论

1.RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子View2次onMeasure
2.RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。
3.在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。
最后再思考一下文章开头那个矛盾的问题,为什么Google给开发者默认新建了个RelativeLayout,而自己却在DecorView中用了个LinearLayout。因为DecorView的层级深度是已知而且固定的,上面一个标题栏,下面一个内容栏。采用RelativeLayout并不会降低层级深度,所以此时在根节点上用LinearLayout是效率最高的。而之所以给开发者默认新建了个RelativeLayout是希望开发者能采用尽量少的View层级来表达布局以实现性能最优,因为复杂的View嵌套对性能的影响会更大一些。

4.能用两层LinearLayout,尽量用一个RelativeLayout,在时间上此时RelativeLayout耗时更小。另外LinearLayout慎用layout_weight,也将会增加一倍耗时操作。由于使用LinearLayout的layout_weight,大多数时间是不一样的,这会降低测量的速度。这只是一个如何合理使用Layout的案例,必要的时候,你要小心考虑是否用layout weight。总之减少层级结构,才是王道,让onMeasure做延迟加载,用viewStub,include等一些技巧。

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码,即可关注。 

如何优化你的布局层级结构之RelativeLayout和LinearLayout及FrameLayout性能分析相关推荐

  1. Android中常见五种布局管理器——RelativeLayout、LinearLayout、FrameLayout、TableLayout、GridLayout

    目录 布局管理器 RelativeLayout 常见属性 Relative的实践操作(实现软件更新界面) LinearLayout 常见属性 LinearLayout的实践操作(模范登录以及微信底部) ...

  2. Android官方开发文档Training系列课程中文版:布局性能优化之布局层级优化

    原文地址:http://android.xsoftlab.net/training/improving-layouts/index.html 引言 布局是直接影响用户体验的关键部分.如果实现的不好,那 ...

  3. android 减少布局层级,Android 布局优化

    布局优化主要从以下几点进行着手 减少布局层次 和 复杂度 优化绘制流程 按需加载布局 减少布局层次 和 复杂度 首先我们可以通过以下工具分析界面布局的结构 查看布局树工具:Hierarchy View ...

  4. 布局检测与优化(一):布局层级优化

    在本文中,我们将全面解析UX中视觉层级的关键原则,探讨如何使用视觉层级来改进产品并优化用户体验. UX中的视觉层级 自设计之初,我们已经使用它来传达重要的信息.设计中的每个元素都应有助于改善用户体验, ...

  5. Android优化五:布局优化

    1.减少布局层级 Google在API文档中建议View树的高度不宜超过10层. 以前我们用Eclipse写代码时,自动生成的模板是以LinearLayout为根节点的,但是后面变成了Relative ...

  6. 网页优化中,网站页面结构该注意什么?

    网站页面结构即网页内容布局,网站页面结构的创建就是要对网页的内容进行规划布局,合理的网站页面结构总是很受搜索引擎蜘蛛的欢迎,网站页面结构也能直接影响页面的用户体验及相关性,还能影响网站整体结构及页面被 ...

  7. CVPR 2022 | CNN自监督预训练新SOTA:上交、Mila、字节联合提出具有层级结构的图像表征自学习新框架...

    ©作者 | 机器之心编辑部 来源 | 机器之心 来自上海交通大学.Mila 魁北克人工智能研究所以及字节跳动的研究者提出了一种具有层级语义结构的自监督表征学习框架,在 ImageNet 数据集上预训练 ...

  8. 顺风车Android性能优化之View布局优化

    一.问题背景 在开发过程中,往往会听到 "性能优化" 这个概念,这个概念很大,比如网络性能优化.耗电量优化等等,对 RD 而言,最容易做的或者是影响最大的,应该是 View 的性能 ...

  9. Android性能优化系列之布局优化,Android程序员校招蚂蚁金服

    25 26 rInflate方法关键代码 void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet ...

  10. 源码分析 merge 标签减少布局层级的秘密(Android Q)

    源码分析 merge 标签减少布局层级的秘密(Android Q) 我在<Android 渲染性能优化--你需要知道的一切!>一文中介绍过,merge 标签用于减少 View 树的层次来优 ...

最新文章

  1. 如何使用PHP解析JSON文件? [重复]
  2. python list同步删除
  3. 强迫症犯了,忍不住赞一下slf4j包Logger.java的优雅代码
  4. AIProCon在线大会笔记之华为涂丹丹:华为云EI,行业智能化升级新引擎
  5. Js 模拟鼠标点击事件
  6. java 热布署 开源_一种简单快捷的 java 热部署方式
  7. 如何在javascript中解析带有两个小数位的浮点数?
  8. 使用 IntraWeb (21) - 基本控件之 TIWTabControl
  9. 深入研究ActivityMQ
  10. 如何制作自己的网课网站 需网课查课插件
  11. oracle instant client卸载,Oracle Instant Client(即时客户端) 安装与配置
  12. [归并排序] 二路归并排序
  13. 中国海洋科技发展迅速 海洋重器世界领先
  14. ubuntu提示根目录存储空间不足的解决办法
  15. 今年林业调查资质丙级与乙级如何办理怎么申报
  16. 域名解析配置 以及 修改DNS服务器(腾讯云域名注册商、阿里云DNS服务器)
  17. 常见堡垒机小知识汇总-行云管家
  18. 舌尖上的广西 | 口馋者争相追求的中国美食梦!!!型男索女尝过吗?
  19. 害!天天在公司写业务代码,面几家挂几家,感觉自己快废了!
  20. Nginx报 No input file specified. 的问题解决之路

热门文章

  1. H5桌面通知: Notification API 的应用
  2. 论文笔记_S2D.57_2018-IROS_LIMO:激光雷达单目视觉里程计
  3. python中读取和保存图片的方法对比
  4. ORB-SLAM3:一个用于视觉、视觉惯性和多地图SLAM系统
  5. 实操教程:Android部署Nanodet模型完成实时高效的物体检测
  6. 思无界实习招聘|移动端SLAM、语义SLAM、三维重建等多个算法岗位
  7. springcloud---how2java--记录零碎的信息
  8. IOS开发之Post 方式获取服务器数据
  9. 【图像处理】C++将读取图像并将图像转换为矩阵的形式
  10. SimpleGIS在线地图插件