2014-09-25 5 views
8

Возможно, большинство разработчиков Android знают, что findViewById - не дешевая операция. Другое дело, что большинство из нас знают, что вы можете повысить производительность, используя наименьшее поддерево иерархии представления, чтобы найти взгляды по их идентификаторам, например:Эффективность 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 с TextView вида на LinearLayout с @+id/some_id_8, следует ли выполнять эту операцию на весь контейнер, или я должен findViewByIdLinearLayout с @+id/some_id_8 и на этом виде findViewById все TextViews?

+0

Можете ли вы объяснить больше своих 2 альтернатив, особенно «findViewById для большего подмножества»? – sotcha

+0

Проверьте мои изменения. – overbet13

+1

[по умолчанию обход] (http://androidxref.com/4.4.4_r1/xref/frameworks/base/core/java/android/view/ViewGroup.java#3246) - это глубина. Поиск поддерева сначала пахнет микро-оптимизацией, что делает код немного сложнее поддерживать. Мера, чтобы узнать, действительно ли это имеет значение. – laalto

ответ

31

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

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

Если производительность критическая, обратитесь к ней, чтобы вы могли позвонить по телефону findViewById() как можно меньше. В Fragment или Activity вы можете посмотреть все 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() просто проверяет, если идентификатор является действительным, и если это то защищенный метод findViewTraversal()View это реализовано так:

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

Он просто проверяет, является ли переданная в идентификатор равен идентификатору View и возвращает 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; 
} 

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

BUT Общее правило заключается в том, что вы должны попытаться уменьшить сложность макета для повышения производительности и что вы должны часто использовать 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> 

И такое расположение, несомненно, дает большой прирост производительности.

+0

Проблема с «Вероятно, большинство разработчиков Android знают, что findViewById - это не дешевая операция» - мало кто, кажется, даже думает, что он на самом деле делает. Даже гуглы собственных мундштуков, похоже, не перестают его рассматривать. , например, широко распространенный шаблон «viewholder» для списка не улучшает ситуацию вообще, если у вас есть одноразрядное количество просмотров в макете, как обычно для элементов списка, и вы используете convertview. одно изменение или изменение текста будет медленнее, чем поиск в режиме просмотра. Я бы хотел, чтобы кто-то оценил это на самом деле. –

+0

'RelativeLayout как-то лучше, чем LinearLayout', это кажется мне 90% неправильным. – natario

+0

@mvai Не 'LinearLayout', много' LinearLayouts' или любой сложный макет. Уверен, что «RelativeLayout» будет работать хуже, чем «LinearLayout» по многим причинам, например, из-за необходимости двух пропусков макета в «RelativeLayouts» и т. Д. Однако, если вы используете «RelativeLayout» для этой цели, для создания - для создания сложные относительные макеты без необходимости вставлять много макетов, тогда «RelativeLayout» действительно сияет. Конечно, сегодня все по-другому. «CoordinatorLayout» с «Behaviors» чрезвычайно гибкий, и очень сложные макеты могут быть реализованы вместе с ним. –

2

Глядя на этот исходный код для Android, я просто не вижу, как findViewById стоит дорого. Это, в основном, простой цикл, я знаю, что он рекурсивный, поэтому вы платите штраф за постоянное переключение стека, но даже самый сложный экран будет в порядке десятков видов не тысяч, за исключением переработанных списков. Я думаю, нам нужны тесты на устройствах с низким уровнем, чтобы знать, действительно ли это проблема.

+0

думаю. Это дорого, пока вы не найдёте findBeId в цикле, таком как listview. – CoXier