Android RelativeLayout和LinearLayout性能分析
今天,簡單講講android里RelativeLayout和LinearLayout的性能比較。
之前,我看到代碼優化時需要將界面扁平化,所以查詢了如何優化解碼,了解一下RelativeLayout和LinearLayout的性能,已經什么情況下使用RelativeLayout和LinearLayout。這里記錄一下。
0.?前言
我們都知道,新建一個Android項目SDK會為我們自動生成的avtivity_main.xml布局文件,然后它的根節點默認是RelativeLayout,在我們的理解里貌似LinearLayout的性能是要比RelativeLayout更優的,比如作為頂級View的DecorView就是個垂直方向的LinearLayout,上面是標題欄,下面是內容欄,我們常用的setContentView()方法就是給內容欄設置布局。那么LinearLayout和RelativeLayout誰的性能更高呢,好吧其實我們都知道前者性能更高,那原因是什么呢?
?
1.???性能對比
一。LinearLayout和RelativeLayout的繪圖的耗時比較:
LinearLayout Measure:0.738ms
Layout:0.176ms
draw:7.655ms
RelativeLayout
Measure:2.280ms
Layout:0.153ms
draw:7.696ms
從這個數據來看無論使用RelativeLayout還是LinearLayout,layout和draw的過程兩者相差無幾,考慮到誤差的問題,幾乎可以認為兩者不分伯仲,關鍵是Measure的過程RelativeLayout卻比LinearLayout慢了一大截。
? RelativewLayout的Measure方法:根據源碼我們發現RelativeLayout會對子View做兩次measure。這是為什么呢?首先RelativeLayout中子View的排列方式是基于彼此的依賴關系,
而這個依賴關系可能和布局中View的順序并不相同,在確定每個子View的位置的時候,就需要先給所有的子View排序一下。 又因為RelativeLayout允許A,B 2個子View,橫向上B依賴A,縱向上A依賴B。所以需要橫向縱向分別進行一次排序測量。LinearLayout的Measure方法:?與RelativeLayout相比LinearLayout的measure就簡單明了的多了,先判斷線性規則,然后執行對應方向上的測量??
LinearLayout和RelativeLayout的measure的小結
從源碼中我們似乎能看出,我們先前的測試結果中RelativeLayout不如LinearLayout快的根本原因是RelativeLayout需要對其子View進行兩次measure過程。而LinearLayout則只需一次measure過程,所以顯然會快于RelativeLayout,但是如果LinearLayout中有weight屬性,則也需要進行兩次measure,但即便如此,應該仍然會比RelativeLayout的情況好一點。
實驗結果我們得之,兩者繪制同樣的界面時layout和draw的過程時間消耗相差無幾,關鍵在于measure過程RelativeLayout比LinearLayout慢了一些。我們知道ViewGroup是沒有onMeasure方法的,這個方法是交給子類自己實現的。因為不同的ViewGroup子類布局都不一樣,那么onMeasure索性就全部交給他們自己實現好了。
下面通過源碼分析一下:
1.1????RelativeLayout的onMeasure分析
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //... 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();applyHorizontalSizeRules(params, myWidth);measureChildHorizontal(child, params, myWidth, myHeight);if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {offsetHorizontalAxis = true;}} }views = mSortedVerticalChildren; count = views.length; for (int i = 0; i < count; i++) {View child = views[i];if (child.getVisibility() != GONE) {LayoutParams params = (LayoutParams) child.getLayoutParams();applyVerticalSizeRules(params, myHeight);measureChild(child, params, myWidth, myHeight);if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {offsetVerticalAxis = true;}if (isWrapContentWidth) {width = Math.max(width, params.mRight);}if (isWrapContentHeight) {height = Math.max(height, params.mBottom);}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);}}}//... }
根據源碼我們發現RelativeLayout會根據2次排列的結果對子View各做一次measure。這是為什么呢?首先RelativeLayout中子View的排列方式是基于彼此的依賴關系,而這個依賴關系可能和Xml布局中View的順序不同,在確定每個子View的位置的時候,需要先給所有的子View排序一下。又因為RelativeLayout允許ViewB在橫向上依賴ViewA,ViewA在縱向上依賴B。所以需要橫向縱向分別進行一次排序測量。
同時需要注意的是View.measure()方法存在以下優化:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||widthMeasureSpec != mOldWidthMeasureSpec ||heightMeasureSpec != mOldHeightMeasureSpec) {...mOldWidthMeasureSpec = widthMeasureSpec;mOldHeightMeasureSpec = heightMeasureSpec; }
即如果我們或者我們的子View沒有要求強制刷新,而父View給子View傳入的值也沒有變化(也就是說子View的位置沒變化),就不會做無謂的測量。RelativeLayout在onMeasure中做橫向測量時,縱向的測量結果尚未完成,只好暫時使用myHeight傳入子View系統。這樣會導致在子View的高度和RelativeLayout的高度不相同時(設置了Margin),上述優化會失效,在View系統足夠復雜時,效率問題就會很明顯。
1.2? LinearLayout的onMeasure過程
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == VERTICAL) { measureVertical(widthMeasureSpec, heightMeasureSpec); } else { measureHorizontal(widthMeasureSpec, heightMeasureSpec); } } //LinearLayout會先做一個簡單橫縱方向判斷,我們選擇縱向這種情況繼續分析 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { //... for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); //... child為空、Gone以及分界線的情況略去//累計權重LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); totalWeight += lp.weight; //計算if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) { //精確模式的情況下,子控件layout_height=0dp且weight大于0無法計算子控件的高度//但是可以先把margin值合入到總值中,后面根據剩余空間及權值再重新計算對應的高度final int totalLength = mTotalLength; mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin); } else { if (lp.height == 0 && lp.weight > 0) { //如果這個條件成立,就代表 heightMode不是精確測量以及wrap_conent模式//也就是說布局是越小越好,你還想利用權值多分剩余空間是不可能的,只設為wrap_content模式lp.height = LayoutParams.WRAP_CONTENT; } // 子控件測量measureChildBeforeLayout(child, i, widthMeasureSpec,0, heightMeasureSpec,totalWeight== 0 ? mTotalLength :0); //獲取該子視圖最終的高度,并將這個高度添加到mTotalLength中final int childHeight = child.getMeasuredHeight(); final int totalLength = mTotalLength; mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); } //... }
源碼中已經標注了一些注釋,需要注意的是在每次對child測量完畢后,都會調用child.getMeasuredHeight()獲取該子視圖最終的高度,并將這個高度添加到mTotalLength中。但是getMeasuredHeight暫時避開了lp.weight>0且高度為0子View,因為后面會將把剩余高度按weight分配給相應的子View。因此可以得出以下結論:
(1)如果我們在LinearLayout中不使用weight屬性,將只進行一次measure的過程。
(2)如果使用了weight屬性,LinearLayout在第一次測量時獲取所有子View的高度,之后再將剩余高度根據weight加到weight>0的子View上。
由此可見,weight屬性對性能是有影響的。
3.???總結論
(1)RelativeLayout慢于LinearLayout是因為它會讓子View調用2次measure過程,而LinearLayout只需一次,但是有weight屬性存在時,LinearLayout也需要兩次measure。
(2)RelativeLayout的子View如果高度和RelativeLayout不同,會導致RelativeLayout在onMeasure()方法中做橫向測量時,縱向的測量結果尚未完成,只好暫時使用自己的高度傳入子View系統。而父View給子View傳入的值也沒有變化就不會做無謂的測量的優化會失效,解決辦法就是可以使用padding代替margin以優化此問題。
(3)在不響應層級深度的情況下,使用Linearlayout而不是RelativeLayout。
結論中的第三條也解釋了文章前言中的問題:DecorView的層級深度已知且固定的,上面一個標題欄,下面一個內容欄,采用RelativeLayout并不會降低層級深度,因此這種情況下使用LinearLayout效率更高。
上面已經將的很清楚了,在層數相同的情況下,使用LinearLayout作為根布局,在復雜布局時,使用RelativeLayout減少界面的層數。
Android RelativeLayout和LinearLayout性能分析就講完了。
就這么簡單。
總結
以上是生活随笔為你收集整理的Android RelativeLayout和LinearLayout性能分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android Comparator的使
- 下一篇: android 解决getColor()