2014-09-25 27 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