Эффективность 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 на более крупном подмножестве? Я думаю, что простой ответ будет заключаться в том, что это зависит от конкретного случая, но, может быть, мы могли бы немного обобщить то, от чего это действительно зависит?

благодаря

РЕДАКТИРОВАТЬ:

Большим подмножеством я имею в виду что-то вроде этого:

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

Solutions Collecting From Web of "Эффективность findViewById"

Это не имеет никакого значения, если вы ищете View напрямую или если вы сначала ищете родителя, а затем ребенка. Но если вы, например, хотите получить три TextViews в LinearLayout с id some_id_8 тогда было бы лучше для производительности, если вы сначала посмотрите на LinearLayout а затем на TextViews . Но разница незначительна. Реальной проблемой является сама компоновка (подробнее об этом ниже).

И вообще findViewById() не является источником всего зла. Это может быть проблемой в ListView если вам нужно вызывать findViewById() возможно, даже несколько раз в течение каждого getView() , но для этого используется шаблон держателя вида.

Когда производительность критически важна, убедитесь, что вы вызываете findViewById() как можно меньше. В Fragment или Activity вы можете искать все Views вам понадобятся в onCreateView() или onCreate() . Если вы сохраните ссылки в нескольких переменных-членах, вам больше не придется называть их.


Теперь, чтобы объяснить, почему findViewById() может быть проблемой производительности, мы должны посмотреть на ее реализацию, эта ссылка приводит к исходному коду Android 4.4.4 :

 public final View findViewById(int id) { if (id < 0) { return null; } return findViewTraversal(id); } 

Таким образом findViewById() просто проверяет, является ли id действительным, и если это тогда, то вызывается защищенный метод findViewTraversal() . В представлении он реализован следующим образом:

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

Он просто проверяет, соответствует ли идентификатор прошедшего в id идентификатору представления и возвращает this если это так, иначе null . Интересной частью является реализация ViewGroup findViewTraversal() , эта ссылка приводит к исходному коду 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 то поиск, который мы ищем, был найден и будет возвращен.

Если вы хотите получить более подробную информацию о том, как работают 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 стоит дорого. Это, в основном, простой цикл, я знаю, что он рекурсивный, поэтому вы платите штраф за постоянное переключение стека, но даже самый сложный экран будет в порядке десятков видов не тысяч, за исключением переработанных списков. Я думаю, нам нужны тесты на устройствах с низким уровнем, чтобы знать, действительно ли это проблема.