大多数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_8
个findViewById
视图,我应该在整个容器上执行此操作,或者我应该LinearLayout
@+id/some_id_8
findViewById
和TextViews
{{1}}所有{{1}}?
答案 0 :(得分:33)
如果您直接寻找View
或者首先寻找父母,然后寻找孩子,那绝对没有区别。但是,如果你想要检索标识为TextViews
的{{1}}中的三个LinearLayout
,那么如果你首先查找some_id_8
,那么性能会更好对于LinearLayout
。但差别很小。真正的问题是布局本身(更多的是进一步向下)。
通常TextViews
不是万恶之源。如果您必须在每次findViewById()
来电期间多次拨打ListView
,这可能是findViewById()
中的一个问题,但这是视图持有者模式的用途。
当性能至关重要时,请注意尽可能少地调用getView()
。在findViewById()
或Fragment
中,您可以在Activity
或Views
中查找所需的所有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
。有趣的部分是null
,this 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
,将被退回。
如果您想了解View
或Views
如何运作的更多详情,建议您自己研究源代码!
所以这一切似乎都非常直截了当。视图层次结构基本上像树一样遍历。这可能会使它相当昂贵或相当快,具体取决于您的布局中有多少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是多么昂贵。它基本上是一个简单的循环,我知道它是递归的,所以你将支付不断切换堆栈的惩罚,但即使是最复杂的屏幕也将是几十个视图而不是数千个,除了回收列表。我想我们需要在低端设备上使用基准来知道它是否真的是一个问题。