Эффективность findViewById, чтобы найти
вероятно, большинство разработчиков Android знают, что findViewById
- это не дешевая операция. Еще одна вещь, которую большинство из нас знает, заключается в том, что вы можете повысить производительность, используя наименьшее поддерево иерархии представлений, чтобы найти представления по их идентификатору, например:
<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
но что происходит, когда иерархия не каскадная, а скорее ветвящаяся на каждом уровне, и вы хотите найти представления скажем, на "листьях"? Вы выполняете findViewById
чтобы добраться до нижней части ветки, найдя родителей, или вы выполняете findViewById
в более широком подмножестве? Я думаю, что простой ответ будет заключаться в том, что это зависит от случая, но, может быть, мы могли бы немного обобщить, от чего это действительно зависит?
спасибо
EDIT:
под большим подмножеством я имею в виду что-то вроде этого:
<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
the TextView
взгляды LinearLayout
С @+id/some_id_8
, должен ли я выполнить эту операцию на всем контейнере, или я должен findViewById
на LinearLayout
С @+id/some_id_8
и на этот взгляд findViewById
все TextViews
?
2 ответов
это не имеет абсолютно никакого значения, если вы ищете View
непосредственно, или если вы смотрите на родителей, а потом ребенка. Но если вы, например, хотите получить три TextViews
на LinearLayout
код some_id_8
тогда было бы лучше для производительности, если вы сначала посмотрите на LinearLayout
и затем на TextViews
. Но разница ничтожна. Реальная проблема сам макет (подробнее об этом дальше).
и вообще findViewById()
не является источником всего зла. Это может быть проблемой в ListView
если вам нужно позвонить findViewById()
возможно даже несколько раз во время каждого getView()
вызов, но это то, для чего предназначен шаблон держателя вида.
когда производительность критична, посмотрите, что вы вызываете findViewById()
как можно меньше. В Fragment
или Activity
вы можете искать все Views
вы всегда будете нуждаться в onCreateView()
или onCreate()
. Если вы сохраните ссылки в нескольких переменных-членах, вам никогда не придется вызывать это снова.
теперь объясни, почему findViewById()
может быть проблемой производительности, мы должны посмотреть на ее реализацию,эта ссылка приводит к исходному коду Android 4.4.4 View:
public final View findViewById(int id) {
if (id < 0) {
return null;
}
return findViewTraversal(id);
}
так findViewById()
просто проверяет, действителен ли идентификатор, и если это тогда защищенный метод findViewTraversal()
называется. В View
он реализован следующим образом:
protected View findViewTraversal(int id) {
if (id == mID) {
return this;
}
return null;
}
он просто проверяет, равен ли переданный 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;
}
первое, если в верхней части этого метода такое же, как в 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
в макете ниже.
и вернуться к исходному вопросу: если вы хотите увеличить производительность, чем уменьшить сложность вашего макета. Нет абсолютно никаких причин иметь так много вложенных 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 стоит дорого. Это в основном простой цикл, я знаю, что он рекурсивный, поэтому вы заплатите штраф за постоянное переключение стека, но даже самый сложный экран будет в порядке десятков просмотров, а не тысяч, за исключением переработанных списков. Я думаю, нам нужны тесты на низких устройствах, чтобы знать, действительно ли это проблема.