android - findViewById 的效率

标签 android performance android-layout findviewbyid

可能大多数 Android 开发者都知道 findViewById不是一个便宜的操作。我们大多数人都知道的另一件事是,您可以通过使用 View 层次结构的最小子树按 id 查找 View 来提高性能,例如:

<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
但是,如果层次结构不是级联的,而是在每个级别上都有分支,并且您想在“叶子”上找到 View ,那么情况会怎样?你执行findViewById通过查找 parent 到达分支的底部,或者您是否执行 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_8 ,我应该对整个容器执行这个操作,还是应该findViewById LinearLayout@+id/some_id_8并在此 View findViewById所有 TextViews ?

最佳答案

如果您查找 View,这绝对没有区别。直接或如果您先寻找 parent ,然后再寻找 child 。但是,例如,如果您想检索三个 TextViewsLinearLayout id some_id_8那么如果您首先查找 LinearLayout,那么性能会更好然后是 TextViews .但差别是微乎其微的。真正的问题是布局本身 (更多关于进一步向下)。

一般findViewById()不是万恶之源。这可能是 ListView 中的问题如需拨打findViewById()每次可能甚至多次getView()调用,但这就是 View 持有者模式的用途。

当性能至关重要时,请调用 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 是否有效,如果有效,则是 protected 方法 findViewTraversal()叫做。在 View它是这样实现的:
protected View findViewTraversal(int id) {
    if (id == mID) {
        return this;
    }
    return null;
}

它只是检查传入的 id 是否等于 View 的 id并返回 this如果是,否则 null .有趣的部分是 findViewTraversal() ViewGroup 的实现, this 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;
}

此方法顶部的第一个 if 与 View 中的相同实现,它只是检查传入的 id 是否等于 ViewGroup 的 id如果它这样做,它会返回自己。之后它遍历所有的 child 并调用 findViewById()在每个 child 上,如果此调用的返回值不是 null然后是 View我们正在寻找的已经找到并将被退回。

如果您想了解更多有关如何操作的详细信息 ViewsViewGroups工作我建议你自己研究源代码!

所以这一切似乎很简单。 View 层次结构本质上像一棵树一样遍历。这可能会使其变得非常昂贵或非常快,具体取决于 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作为 child :
<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的数量在布局较低。这同样适用于几乎所有其他与 View 相关的过程,如绘图、布局、测量。一切都会更快,因为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>

而这样的布局肯定会产生很大的性能提升。

关于android - findViewById 的效率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26037744/

相关文章:

java - ScrollView 不是从顶部开始

android - 如何锁定对象/成为监视器的所有者 (Android)

java - 如何使用其他项目的共享库模块在 Android Studio 中运行应用程序?

android - ExpandableListView,如何设置子高度?

java - 以非常小的插入将大量数据插入数据库

mysql - 从两个 mysql 表中查找字谜

Android:XML 文件的设计 View 为白色和空白

android - 从图库访问图片时无法设置图片来源-Android

android - 在 AmfPHP 中开发的 Web 服务——在 IOS 和 Android 中使用?

javascript - 制作我自己的 slider