2014-09-25 26 views
8

可能大多數的Android開發者知道findViewById是不是一個便宜的操作。我們大多數人知道的另一件事情是,你可以通過使用視圖層次的最小的子樹查找瀏覽用自己的ID,例如提高性能:效率findViewById

<LinearLayout 
    android:id="@+id/some_id_0"> 
    <LinearLayout 
     android:id="@+id/some_id_1"> 
     <LinearLayout 
      android:id="@+id/some_id_2"> 
      <TextView android:id="@+id/textview" /> 
     </LinearLayout> 
    </LinearLayout> 
</LinearLayout> 

在這種情況下,你可能想要搜索在LinearLayoutid == @id/textview

但什麼是這樣,當層級沒有級聯,而是分支在各個層面上,你想找到的「葉子」讓我們說的意見?您是否通過尋找父母來執行findViewById以達到分支的底部,或者您是否在更大的子集上執行findViewById?我認爲一個簡單的答案將取決於案例,但也許我們可以概括一下它真正依賴於什麼?

感謝

編輯:

通過一個較大的子集,我的意思是這樣的:

<RelativeLayout android:id="@+id/r0">  
    <RelativeLayout 
     android:id="@+id/r1"> 
     <LinearLayout 
      android:id="@+id/some_id_0"> 
      <LinearLayout 
       android:id="@+id/some_id_1"> 
       <LinearLayout 
        android:id="@+id/some_id_2"> 
        <TextView android:id="@+id/textview0" /> 
       </LinearLayout> 
      </LinearLayout> 
     </LinearLayout> 
     <LinearLayout 
      android:id="@+id/some_id_3"> 
      <LinearLayout 
       android:id="@+id/some_id_4"> 
       <LinearLayout 
        android:id="@+id/some_id_5"> 
        <TextView android:id="@+id/textview1" /> 
       </LinearLayout> 
      </LinearLayout> 
     </LinearLayout> 
    </RelativeLayout> 
    <LinearLayout 
     android:id="@+id/some_id_6"> 
     <LinearLayout 
      android:id="@+id/some_id_7"> 
      <LinearLayout 
       android:id="@+id/some_id_8"> 
       <TextView android:id="@+id/textview2" /> 
       <TextView android:id="@+id/textview3" /> 
       <TextView android:id="@+id/textview4" /> 
      </LinearLayout> 
     </LinearLayout> 
    </LinearLayout> 
</RelativeLayout> 

所以問題是,如果我想使用findViewByIdTextView意見LinearLayout@+id/some_id_8,我應該在整個容器上執行這個操作,還是我應該findViewByIdLinearLayout@+id/some_id_8和在這個視圖findViewById所有的TextViews

+0

你能解釋更多的你的2個替代品,特別是「在更大的子集上的findViewById」嗎? – sotcha 2014-09-29 14:37:57

+0

請檢查我的編輯。 – overbet13 2014-09-30 09:29:11

+1

[默認遍歷](http://androidxref.com/4.4.4_r1/xref/frameworks/base/core/java/android/view/ViewGroup.java#3246)是深度優先。首先發現一個子樹就像微型優化,這使代碼稍微難以維護。測量以確定它是否真的有所作用 – laalto 2014-09-30 09:43:03

回答

31

它使絕對沒有什麼區別,如果你直接或者如果你找一個父母第一,那麼孩子去找View。但是,如果你比如要與ID some_id_8檢索在LinearLayout三個TextViews那麼這將是獲得更好的性能,如果你先來說一下LinearLayout然後爲TextViews。但差異很小。真正的問題是佈局本身(更進一步下來)。

而且一般findViewById()是不是所有的罪惡之源。如果您在每個getView()調用期間必須撥打findViewById()幾次,那麼這可能是ListView中的問題,但這正是查看所有者模式的用途。

當性能是至關重要的看到它,你叫findViewById()儘可能少。在FragmentActivity你可以看看所有的Views你永遠都需要在onCreateView()onCreate()。如果將引用保存在幾個成員變量中,則不必再次調用它。


我們解釋爲什麼findViewById()可以是一個性能問題,我們來看看它的實現,this link leads to the Android 4.4.4 View source code

public final View findViewById(int id) { 
    if (id < 0) { 
     return null; 
    } 
    return findViewTraversal(id); 
} 

所以findViewById()只檢查如果ID是有效的,如果是,則調用受保護的方法findViewTraversal()。在View它的實現是這樣的:

protected View findViewTraversal(int id) { 
    if (id == mID) { 
     return this; 
    } 
    return null; 
} 

它只是檢查,如果在ID傳遞等於View的ID,如果它返回this,否則null。有趣的部分是findViewTraversal()實施ViewGroupthis links leads to the Android 4.4.4 ViewGroup source code

protected View findViewTraversal(int id) { 
    if (id == mID) { 
     return this; 
    } 

    final View[] where = mChildren; 
    final int len = mChildrenCount; 

    for (int i = 0; i < len; i++) { 
     View v = where[i]; 

     if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { 
      v = v.findViewById(id); 

      if (v != null) { 
       return v; 
      } 
     } 
    } 

    return null; 
} 

第一,如果在此方法的頂部是一樣的,在View執行,它只是檢查,如果在ID傳遞等於的標識ViewGroup,如果它確實返回自身。之後,它遍歷所有的孩子,並呼籲每個孩子findViewById(),如果這個電話的返回值不是null那麼我們正在尋找的View已找到並將返回。

如果您想了解ViewsViewGroups工作方式的更多詳細信息,我建議您自己研究源代碼!


所以這一切看起來都非常簡單。視圖層次本質上像樹一樣遍歷。這可能會使其非常昂貴或相當快,具體取決於您的佈局中有多少個Views

<LinearLayout android:id="@+id/some_id_0"> 
    <LinearLayout android:id="@+id/some_id_1"> 
     <LinearLayout android:id="@+id/some_id_2"> 
      <TextView android:id="@+id/textview0" /> 
     </LinearLayout> 
    </LinearLayout> 
</LinearLayout> 

或者,如果它看起來像這樣:

<LinearLayout android:id="@+id/some_id_0"> 
    <LinearLayout android:id="@+id/some_id_1" /> 
    <LinearLayout android:id="@+id/some_id_2" /> 
    <TextView android:id="@+id/textview0" /> 
</LinearLayout> 

由於Views量在這兩種情況下是相同的,並且findViewById()的性能,如果你的佈局看起來像這樣沒關係與Views的數量成比例。

但是一般規則是您應該儘量減少佈局的複雜性以提高性能,並且您應該經常使用RelativeLayout。這種方法的作用僅僅是因爲如果你降低了複雜性,你也可以在佈局中減少Views的數量,而RelativeLayouts非常適合降低複雜性。讓我說明的是,像你這樣的佈局:

<LinearLayout android:id="@+id/some_id_0"> 
    <RelativeLayout android:id="@+id/some_id_5"> 
     <LinearLayout android:id="@+id/some_id_1" /> 
     <LinearLayout android:id="@+id/some_id_2" /> 
    </RelativeLayout> 
    <RelativeLayout android:id="@+id/some_id_6"> 
     <LinearLayout android:id="@+id/some_id_3" /> 
     <LinearLayout android:id="@+id/some_id_4" /> 
    </RelativeLayout> 
</LinearLayout> 

試想一下,在這種情況下,兩個以上的RelativeLayouts都只是存在於一些特殊的方式,內LinearLayouts和外LinearLayout位置正好在那裏將RelativeLayouts放置在彼此的下方。你可以很容易建立,只需RelativeLayout爲根,四LinearLayouts兒童相同的佈局:

<RelativeLayout android:id="@+id/some_id_0"> 
    <LinearLayout android:id="@+id/some_id_1" /> 
    <LinearLayout android:id="@+id/some_id_2" /> 
    <LinearLayout android:id="@+id/some_id_3" /> 
    <LinearLayout android:id="@+id/some_id_4" /> 
</RelativeLayout> 

這佈局的性能會比上面沒有佈局更好,因爲RelativeLayout是某種性能 - 明智地比LinearLayout更好,而不是因爲佈局更平坦,而僅僅是因爲佈局中Views的數量更低。幾乎所有其他與視圖相關的過程(如繪圖,佈局,測量)也是如此。一切都會更快,因爲佈局中Views的數量較低。


並返回到您的原始問題:如果您想要提高性能而不是降低佈局的複雜性。絕對沒有理由有這麼多嵌套LinearLayouts。你的「大集」,可以幾乎肯定會減少到這一點:

<RelativeLayout android:id="@+id/r0"> 
    <TextView android:id="@+id/textview0" /> 
    <TextView android:id="@+id/textview1" /> 
    <TextView android:id="@+id/textview2" /> 
    <TextView android:id="@+id/textview3" /> 
    <TextView android:id="@+id/textview4" /> 
</RelativeLayout> 

而這樣的佈局肯定會產生很大的性能提升。

+0

「很可能大多數Android開發人員知道findViewById不是一個便宜的操作」的問題是,很少有人似乎甚至認爲它實際上做了什麼。即使谷歌自己的喉舌似乎也沒有停下來考慮它。 例如,如果您在佈局中有一個單位數量的視圖,那麼listview項目中通常使用convertview的情況下,listview項目中通常使用listview項目時,大量吹捧的「viewholder」模式根本不會改善事情。單個可繪製或文本更改將比查看搜索更慢。我希望有人能夠真正地對其進行基準測試.. – 2016-04-18 05:11:27

+0

RelativeLayout在某種程度上比LinearLayout更好,這對我來說似乎錯誤90%。 – natario 2016-05-04 14:54:12

+0

@mvai不是'LinearLayout',許多'LinearLayouts'或任何複雜的佈局。出於很多原因,「RelativeLayout」肯定會比「LinearLayout」更糟糕,例如需要在「RelativeLayouts」中進行兩次佈局傳遞等。但是,如果您使用「RelativeLayout」來達到目的 - 創建複雜的相對佈局,而無需嵌套許多佈局,那麼'RelativeLayout'確實會發光。當然,今天一切都不一樣。具有「Behaviors」的'CoordinatorLayout'極其靈活,佈局非常複雜,可以輕鬆實現。 – 2016-05-04 14:58:38

2

看看那個android源代碼我只是沒有看到findViewById是如何昂貴的。這基本上是一個簡單的循環,我知道它是遞歸的,所以你會付出不斷切換堆棧的代價,但即使是最複雜的屏幕將按照幾十個視圖的順序而不是數千個,除了循環列表。我想我們需要測試低端設備的基準,以確定它是否真的存在問題。

+0

我這麼認爲。直到在listview這樣的循環中找到ViewById纔是昂貴的。 – CoXier 2017-11-26 03:06:20