findViewById的效率

时间:2014-09-25 11:41:59

标签: android performance android-layout findviewbyid

大多数Android开发者可能都知道findViewById不是一个廉价的操作。我们大多数人都知道的另一件事是,您可以通过使用视图层次结构的最小子树来查找其ID的视图来提升性能,例如:

<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>

在这种情况下,您可能希望使用LinearLayout

id == @id/textview中进行搜索

但是,如果层次结构不是级联,而是在每个级别上进行分支,并且您希望在&#34;离开&#34;我们说吧?您是通过寻找父母来执行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>

所以问题是,如果我想findViewById TextView LinearLayout @+id/some_id_8findViewById视图,我应该在整个容器上执行此操作,或者我应该LinearLayout @+id/some_id_8 findViewByIdTextViews {{1}}所有{{1}}?

2 个答案:

答案 0 :(得分:33)

如果您直接寻找View或者首先寻找父母,然后寻找孩子,那绝对没有区别。但是,如果你想要检索标识为TextViews的{​​{1}}中的三个LinearLayout,那么如果你首先查找some_id_8,那么性能会更好对于LinearLayout。但差别很小。真正的问题是布局本身(更多的是进一步向下)。

通常TextViews不是万恶之源。如果您必须在每次findViewById()来电期间多次拨打ListView,这可能是findViewById()中的一个问题,但这是视图持有者模式的用途。

当性能至关重要时,请注意尽可能少地调用getView()。在findViewById()Fragment中,您可以在ActivityViews中查找所需的所有onCreateView()。如果将引用保存在几个成员变量中,则永远不必再次调用它。


现在解释为什么onCreate()可能是性能问题,我们必须查看其实现,this link leads to the Android 4.4.4 View source code

findViewById()

因此public final View findViewById(int id) { if (id < 0) { return null; } return findViewTraversal(id); } 只检查id是否有效,如果是,则调用受保护的方法findViewById()。在findViewTraversal()中,它实现如下:

View

它只是检查传入的id是否等于protected View findViewTraversal(int id) { if (id == mID) { return this; } return null; } 的id,如果是,则返回View,否则this。有趣的部分是nullthis links leads to the Android 4.4.4 ViewGroup source code的实施findViewTraversal()

ViewGroup

此方法顶部的第一个if与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; } 实现中的相同,它只检查传入的id是否等于View的id,如果是,则返回自身。之后,它遍历所有孩子,并在每个孩子上调用ViewGroup,如果此调用的返回值不是findViewById(),则找到我们正在查找的null,将被退回。

如果您想了解ViewViews如何运作的更多详情,建议您自己研究源代码!


所以这一切似乎都非常直截了当。视图层次结构基本上像树一样遍历。这可能会使它相当昂贵或相当快,具体取决于您的布局中有多少ViewGroups。如果您的布局如下所示并不重要:

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。你的&#34;大子集&#34;几乎可以肯定会减少到这个:

LinearLayouts

这样的布局肯定会带来很大的性能提升。

答案 1 :(得分:2)

看看那个android源代码,我根本看不出findViewById是多么昂贵。它基本上是一个简单的循环,我知道它是递归的,所以你将支付不断切换堆栈的惩罚,但即使是最复杂的屏幕也将是几十个视图而不是数千个,除了回收列表。我想我们需要在低端设备上使用基准来知道它是否真的是一个问题。