1. 程式人生 > >如何優化你的佈局層級結構之RelativeLayout和LinearLayout

如何優化你的佈局層級結構之RelativeLayout和LinearLayout

工作一段時間後,經常會被領導說,你這個進入速度太慢了,競品的進入速度很快,你搞下優化吧?每當這時,你會怎麼辦?功能實現都有啊,進入時要載入那麼多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()方法
[java] view plain copy  print?
  1. @Override
  2.    protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.        if (mDirtyHierarchy) {  
  4.            mDirtyHierarchy = false;  
  5.            sortChildren();  
  6.        }  
  7.        int myWidth = -1;  
  8.        int myHeight = -1;  
  9.        int width = 0;  
  10.        int height = 0;  
  11.        finalint widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  12.        finalint heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  13.        finalint widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  14.        finalint heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  15.        // Record our dimensions if they are known;
  16.        if (widthMode != MeasureSpec.UNSPECIFIED) {  
  17.            myWidth = widthSize;  
  18.        }  
  19.        if (heightMode != MeasureSpec.UNSPECIFIED) {  
  20.            myHeight = heightSize;  
  21.        }  
  22.        if (widthMode == MeasureSpec.EXACTLY) {  
  23.            width = myWidth;  
  24.        }  
  25.        if (heightMode == MeasureSpec.EXACTLY) {  
  26.            height = myHeight;  
  27.        }  
  28.        View ignore = null;  
  29.        int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;  
  30.        finalboolean horizontalGravity = gravity != Gravity.START && gravity != 0;  
  31.        gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;  
  32.        finalboolean verticalGravity = gravity != Gravity.TOP && gravity != 0;  
  33.        int left = Integer.MAX_VALUE;  
  34.        int top = Integer.MAX_VALUE;  
  35.        int right = Integer.MIN_VALUE;  
  36.        int bottom = Integer.MIN_VALUE;  
  37.        boolean offsetHorizontalAxis = false;  
  38.        boolean offsetVerticalAxis = false;  
  39.        if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {  
  40.            ignore = findViewById(mIgnoreGravity);  
  41.        }  
  42.        finalboolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;  
  43.        finalboolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;  
  44.        // We need to know our size for doing the correct computation of children positioning in RTL
  45.        // mode but there is no practical way to get it instead of running the code below.
  46.        // So, instead of running the code twice, we just set the width to a "default display width"
  47.        // before the computation and then, as a last pass, we will update their real position with
  48.        // an offset equals to "DEFAULT_WIDTH - width".
  49.        finalint layoutDirection = getLayoutDirection();  
  50.        if (isLayoutRtl() && myWidth == -1) {  
  51.            myWidth = DEFAULT_WIDTH;  
  52.        }  
  53.        View[] views = mSortedHorizontalChildren;  
  54.        int count = views.length;  
  55.        for (int i = 0; i < count; i++) {  
  56.            View child = views[i];  
  57.            if (child.getVisibility() != GONE) {  
  58.                LayoutParams params = (LayoutParams) child.getLayoutParams();  
  59.                int[] rules = params.getRules(layoutDirection);  
  60.                applyHorizontalSizeRules(params, myWidth, rules);  
  61.                measureChildHorizontal(child, params, myWidth, myHeight);  
  62.                if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {  
  63.                    offsetHorizontalAxis = true;  
  64.                }  
  65.            }  
  66.        }  
  67.        views = mSortedVerticalChildren;  
  68.        count = views.length;  
  69.        finalint targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;  
  70.        for (int i = 0; i < count; i++) {  
  71.            final View child = views[i];  
  72.            if (child.getVisibility() != GONE) {  
  73.                final LayoutParams params = (LayoutParams) child.getLayoutParams();  
  74.                applyVerticalSizeRules(params, myHeight, child.getBaseline());  
  75.                measureChild(child, params, myWidth, myHeight);  
  76.                if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {  
  77.                    offsetVerticalAxis = true;  
  78.                }  
  79.                if (isWrapContentWidth) {  
  80.                    if (isLayoutRtl()) {  
  81.                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {  
  82.                            width = Math.max(width, myWidth - params.mLeft);  
  83.                        } else {  
  84.                            width = Math.max(width, myWidth - params.mLeft - params.leftMargin);  
  85.                        }  
  86.                    } else {  
  87.                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {  
  88.                            width = Math.max(width, params.mRight);  
  89.                        } else {  
  90.                            width = Math.max(width, params.mRight + params.rightMargin);  
  91.                        }  
  92. 相關推薦

    如何優化佈局層級結構RelativeLayoutLinearLayout及FrameLayout效能分析

    工作一段時間後,經常會被領導說,你這個進入速度太慢了,競品的進入速度很快,你搞下優化吧?每當這時,你會怎麼辦?功能實現都有啊,進入時要載入那麼多view,這也沒辦法啊,等等。先看一些現象吧:用Android studio,新建一個Activity自動生成的佈局檔案都是RelativeLayout,或許你會認為

    如何優化佈局層級結構RelativeLayoutLinearLayout

    工作一段時間後,經常會被領導說,你這個進入速度太慢了,競品的進入速度很快,你搞下優化吧?每當這時,你會怎麼辦?功能實現都有啊,進入時要載入那麼多view,這也沒辦法啊,等等。 先看一些現象吧:用Android studio,新建一個Activity自動生成的佈局檔案都是RelativeLayou

    資料結構——陣列連結串列

    1. 陣列 1.1 陣列為什麼從零編號? 陣列名代表陣列的首地址,陣列的下標其實代表陣列中某個元素相對首地址的偏移量,陣列的第一個元素是零偏移,因此從 0 開始。 上面其實也只是一個解釋, C 語言設計者用零開始編號,後來的各種語言也便紛紛效仿,因此就形成了這個習慣。 1

    資料結構佇列

    棧和佇列是兩種重要的線性結構。從資料結構角度來看,棧和佇列也是線性表,它們是操作受限的線性表,被稱為限定性的資料結構。 棧(Stack) 棧是限定僅在表尾進行插入或刪除操作的線性表。 表尾端被稱為棧頂(top),表頭端稱為棧底(bottom),不含元素的空表稱為空棧。

    資料結構二叉樹

    樹型結構是一類重要的非線性資料結構,樹是以分支關係定義的層次結構。 樹(Tree) 樹是n(n>=0)個結點的有限集。 在任意一棵非空樹中: (1)有且僅有一個特定的根結點(Root) (2)當n>1時,其餘節點可分為m(m>0)個互不相交的有限集

    Ted 帶學習資料結構 二叉堆(Binary Heap)

    二叉堆(Binary Heap) (1)structure property Heap(堆)是一個除了底層節點外的完全填滿的二叉樹,底層可以不完全,左到右填充節點。(a heap is a binar

    python數據結構數字字符串

    指定位置 baidu tle 簡單 str2 2.4 邏輯運算符 多個 mat python數據類型: Number(數字) String(字符串) List(列表) Dictonary(字典) Tuple(元組) sets(集合) 其中數字、字符串、元組是不可變的,列

    Android Studio建立RelativeLayoutLinearLayout佈局layout檔案

    Android Studio新建專案,然後大麥main_activity佈局檔案,是這樣的 <?xml version="1.0" encoding="utf-8"?> <andro

    redis的資料結構 String

    請允許我拽一句文化詞兒 工欲善其事必先利其器。 這裡的器就是我們redis的根本 有什麼樣的資料結構決定了它適合做什麼樣的事兒。 ------------------------------------------------------------分割線---

    Redis資料結構map set sortedset

    map的結構是典型的字典結構 他的命令是H開頭的一些命令 hset 、hget 、hexists (用來判斷是否存在某個欄位 返回值是1 說明存在) 用途: 可以用來儲存類似物件的資料 一定要注意value不能 巢狀其他型別了 map的資料結構 在dict.

    Cris 複習Python日記(四):Python 資料結構序列列表

    1. 簡單認識序列和列表 # 序列 # 序列是Python 中最基本的資料結構,用於儲存一組有序的資料,所有資料都在序列中擁有一個唯一索引,並且按照元素新增的順序來指定序列 # 序列的分類 # 1.

    python資料結構佇列

    1.功能實現 之前文章有,可以點開看看 棧 佇列 2.應用(1)括號匹配及字尾表示式 class Solution(object): def isValid(self, s): """ :type s: str :rtype

    ES6資料結構SetMap

    Set (1)基本用法: ES6 提供了新的資料結構 Set。它類似於陣列,但是成員的值都是唯一的,沒有重複的值。 Set 本身是一個建構函式,用來生成 Set 資料結構。 Set 函式可以接受一個數組(或者具有 iterable 介面的其他資料結構)作為引數,用來初始化。

    資料結構陣列廣義表

    1. 陣列的順序儲存表示:用一維陣列按約定次序(一般為行序)來表示多維陣列。 #include <iostream> #include <stdarg.h> using namespace std; #define OK 1 #define ERROR -1 #

    Go 語言結構指標

    前言 本系列文章總共包括4篇,主要幫助大家理解Go語言中一些語法結構和其背後的設計原則,包括指標、棧、堆、指標逃逸分析和值傳遞/地址傳遞。這一篇是本系列的第一篇,主要介紹棧和指標 以下是本系列文章的索引 1) Go語言結構之棧與指標 2) Go語言結構之

    RelativeLayoutLinearLayout效能比較 相對佈局線性佈局的效能比較

     看到幾篇關於RelativeLayout和LinearLayout效能分析的部落格,寫的相當不錯,這裡在大神的基礎上,增加了部分內容      RelativeLayout和LinearLayout是Android中常用的佈局,兩者的使用會極大的影響程式生成每一幀的效能,因此,正確的使用它們是提升

    python 資料結構佇列

    # 棧是一種特殊的線性表,僅能線上性表的一端操作,棧頂允許操作,棧底不允許操作。 # 棧的特性:後進先出 class Stack(object): """棧""" def __init__(self): self.items=[] def is_empty(s

    深入理解STLStackQueue

    上一篇部落格,帶你深入理解STL之Deque容器中詳細介紹了deque容器的原始碼實現方式。結合前面介紹的兩個容器vector和list,在使用的過程中,我們確實要知道在什麼情況下需要選擇恰當的容器來滿足需求和提升效率。一般選擇的準則有如下幾條: 如果需要隨

    Algorithm——簡單資料結構佇列連結串列(十三)

    Algorithm——簡單資料結構之佇列和連結串列佇列是一種先進先出策略,而連結串列中的各元素按線性順序排列。陣列的線性順序是由陣列的下標決定的,但連結串列的順序是由各個物件裡的指標決定的。佇列有入隊和出隊操作,連結串列則有插入、刪除、查詢表中節點的操作。佇列和雙向連結串列的

    js資料結構佇列

    棧是一種遵從後進先出(LIFO)原則的有序集合。新新增的或待刪除的元素都儲存在棧末尾,稱作棧頂,另一端稱作棧底。在棧裡,新元素都靠近棧頂,舊元素就接近棧底。 佇列是遵循先進先出(FIFO)原則的一組有序的項。佇列在尾部新增新元素,並從頂部移除元素。最新新增的元