findViewById的效率

ove*_*t13 13 performance android 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>
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你可能希望在搜索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>
Run Code Online (Sandbox Code Playgroud)

所以现在的问题是,如果我想findViewByIdTextView视图中LinearLayout使用@+id/some_id_8,我应该在整个容器上执行此操作,或者我应该findViewByIdLinearLayout使用@+id/some_id_8,并在此视图findViewById所有TextViews

Xav*_*ler 35

如果你View直接寻找,或者如果你首先寻找父母,然后寻找孩子,那么它绝对没有区别.但是,如果您想要使用id 检索三个TextViews,那么如果您首先查找然后查找,那么性能会更好.但差别很小.真正的问题是布局本身(更多的是进一步向下).LinearLayoutsome_id_8LinearLayoutTextViews

而且一般findViewById()不是万恶之源.ListView如果你findViewById()在每次getView()调用期间都需要多次调用,这可能是一个问题,但这就是视图持有者模式的用途.

当性能至关重要时,请确保您findViewById()尽可能少地打电话.在一个FragmentActivity你可以找到Views你需要的所有onCreateView()onCreate().如果将引用保存在几个成员变量中,则永远不必再次调用它.


现在解释为什么findViewById()可能是性能问题我们必须看看它的实现,这个链接导致Android 4.4.4查看源代码:

public final View findViewById(int id) {
    if (id < 0) {
        return null;
    }
    return findViewTraversal(id);
}
Run Code Online (Sandbox Code Playgroud)

所以findViewById()只检查id是否有效,如果是,则findViewTraversal()调用protected方法.在View它实现如下:

protected View findViewTraversal(int id) {
    if (id == mID) {
        return this;
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

它只检查传入的id是否等于id的id,如果是,则View返回this,否则返回null.有趣的部分是findViewTraversal()执行ViewGroup,这个链接导致Android 4.4.4 ViewGroup源代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

这个方法顶部的第一个if与View实现中的相同,只是检查传入的id是否等于id的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>
Run Code Online (Sandbox Code Playgroud)

或者,如果它看起来像这样:

<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>
Run Code Online (Sandbox Code Playgroud)

因为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>
Run Code Online (Sandbox Code Playgroud)

想象一下,在这种情况下,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>
Run Code Online (Sandbox Code Playgroud)

并且该布局的性能将优于上面的布局,因为它RelativeLayout在某种程度上在性能方面优于a 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>
Run Code Online (Sandbox Code Playgroud)

而这样的布局肯定会带来巨大的性能提升.