findViewById的效率 [英] Efficiency of findViewById

查看:90
本文介绍了findViewById的效率的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

可能大多数Android开发人员都知道findViewById并不是便宜的操作.我们大多数人都知道的另一件事是,您可以通过使用视图层次结构中最小的子树通过其ID查找视图来提高性能,例如:

Probably most Android devs know that findViewById is not a cheap operation. Another thing that most of us know, is that you can boost the performance by using the smallest sub-tree of the view hierarchy to find views by their id, example:

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

在这种情况下,您可能想用id == @id/textview

In this case you probably want to search in the LinearLayout with the id == @id/textview

但是,当层次结构不是级联而是在每个级别上分支,而您想在叶子"上找到视图时,情况又如何呢?您是通过查找父级来执行findViewById到达分支的底部,还是在更大的子集上执行findViewById?我认为一个简单的答案是,它取决于情况,但是也许我们可以归纳一下它真正取决于什么?

But what is the case when the hierarchy is not cascading, but rather branching on every level, and you want to find the views on the "leaves" let's say? Do you perform the findViewById to get to the bottom of the branch by finding parents, OR do you perform the findViewById on a larger subset? I think a simple answer would be that it depends on the case, but maybe we could generalize a bit on what it really depends?

谢谢

通过更大的子集,我的意思是这样的:

By a larger subset I mean something like this:

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

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

So the question would be if I want to findViewById the TextView views in LinearLayout with @+id/some_id_8, should I perform this operation on the whole container, or I should findViewById the LinearLayout with @+id/some_id_8 and on this view findViewById all the TextViews?

推荐答案

如果直接寻找View,或者先寻找父母,然后再寻找孩子,那绝对没有区别.但是,例如,如果要检索ID为some_id_8LinearLayout中的三个TextViews,则最好先查找LinearLayout,然后再查找TextViews,以获得更好的性能.但是区别很小.真正的问题是布局本身(有关更多内容,请参见下文).

It makes absolutely no difference if you look for the View directly or if you look for a parent first and then the child. But if you for example want to retrieve the three TextViews in the LinearLayout with the id some_id_8 then it would be better for performance if you first look for the LinearLayout and then for the TextViews. But the difference is miniscule. The real problem is the layout itself (more on that further down).

通常,findViewById()并非万恶之源.如果您必须在每次getView()调用中甚至多次调用findViewById(),则在ListView中可能会出现问题,但这就是视图持有者模式的用途.

And generally findViewById() is not the source of all evil. It can be a problem in a ListView if you have to call findViewById() possibly even several times during each getView() call, but that's what the view holder pattern is for.

当性能至关重要时,请尽可能少地调用findViewById().在FragmentActivity中,您可以在onCreateView()onCreate()中查找所需的所有Views.如果将引用保存在几个成员变量中,则无需再次调用它.

When performance is critical see to it that you call findViewById() as little as possible. In a Fragment or Activity you can look for all the Views you will ever need in onCreateView() or onCreate(). If you save the references in a few member variables you will never have to call it again.

现在要解释为什么findViewById()可能是性能问题,我们必须查看其实现,

Now to explain why findViewById() can be a performance problem we have to look at its implementation, 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中,它是这样实现的:

So findViewById() just checks if the id is valid, and if it is then the protected method findViewTraversal() is called. In a View it is implemented like this:

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

它只是检查传入的id是否等于View的id,如果是,则返回this,否则返回null.有趣的部分是ViewGroupfindViewTraversal()实现,

It just checks if the passed in id is equal to the id of the View and returns this if it does, otherwise null. The interesting part is the findViewTraversal() implementation of 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;
}

此方法顶部的第一个(如果)与View实现中的相同,它只是检查传入的id是否等于ViewGroup的id,是否返回自身.之后,它将遍历所有子项并在每个子项上调用findViewById(),如果此调用的返回值不是null,则将找到并寻找我们要查找的View.

The first if at the top of this method is the same as in the View implementation, it just checks if the passed in id equals the id of the ViewGroup and if it does it returns itself. After that it loops through all the children and calls findViewById() on each of the children, if the return value of this call is not null then the View we are looking for has been found and will be returned.

如果您想了解有关ViewsViewGroups工作方式的更多信息,建议您自己研究源代码!

If you want more details about how Views or ViewGroups work I suggest you study the source code yourself!

因此,这一切似乎很简单.视图层次结构基本上像树一样遍历.这可能会使它变得非常昂贵或相当快,具体取决于布局中有多少Views.布局是否像这样都没关系:

So this all seems pretty straight forward. The view hierarchy is essentially traversed like a tree. And that can make it pretty expensive or pretty fast depending on how many Views are in your layout. It doesn't matter if your layout looks like this:

<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的量而缩放.

Because the amount of Views is the same in both cases, and the performance of findViewById() scales with the amount of the Views.

但是的一般规则是,您应尝试降低布局的复杂性以提高性能,并且应经常使用RelativeLayout.这之所以起作用,是因为如果您降低了复杂性,那么您还减少了布局中的Views数量,并且RelativeLayouts非常擅长降低复杂性.让我说明一下,您的图像布局如下:

BUT the general rule is that you should try to reduce the complexity of the layout to increase performance and that you should often use a RelativeLayout. And that works just because if you reduce the complexity you also reduce the amount of Views in the layout and RelativeLayouts are very good at reducing complexity. Let me illustrate that, image you have a layout like this:

<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用作子代:

Imagine that in this case both of the RelativeLayouts above are just there to position the inner LinearLayouts in some special way and the outer LinearLayout is just there to position the RelativeLayouts below each other. You can very easily build the same layout with just a RelativeLayout as a root and the four LinearLayouts as children:

<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的数量较少,一切都会变得更快.

And the performance of that layout will be better than the layout above not because the RelativeLayout is somehow performance-wise better than a LinearLayout and not because the layout is flatter, but simply because the amount of Views in the layout is lower. The same applies for almost all other view-related processes like drawing, layouting, measuring. Everything will be faster just because the amount of Views in the layout is lower.

并回到您的原始问题:如果要提高性能而不是降低布局的复杂性.绝对没有理由要嵌套许多LinearLayouts.您的大子集"几乎可以确定为:

And to return to your original question: If you want a performance increase than reduce the complexity of your layout. There is absolutely no reason to have so many nested LinearLayouts. Your "large subset" can almost certainly be reduced to this:

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

这样的布局肯定会大大提高性能.

And such a layout would definitely yield a big performance boost.

这篇关于findViewById的效率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆