Иерархии производительности и просмотра

То, как вы управляете иерархией объектов View , может существенно повлиять на производительность вашего приложения. На этой странице описывается, как оценить, замедляет ли ваша иерархия представлений работу вашего приложения, и предлагаются некоторые стратегии решения проблем, которые могут возникнуть.

На этой странице основное внимание уделяется улучшению макетов на основе View . Сведения об улучшении производительности Jetpack Compose см. в разделе Производительность Jetpack Compose .

Планирование и измерение производительности

Конвейер рендеринга включает этап компоновки и измерения , во время которого система соответствующим образом размещает соответствующие элементы в иерархии представлений. Часть измерения на этом этапе определяет размеры и границы объектов View . Часть макета определяет, где на экране расположить объекты View .

Оба этих этапа конвейера требуют небольших затрат на каждое обрабатываемое представление или макет. В большинстве случаев эти затраты минимальны и не оказывают заметного влияния на производительность. Однако оно может быть больше, когда приложение добавляет или удаляет объекты View , например, когда объект RecyclerView перерабатывает или повторно использует их. Стоимость также может быть выше, если объекту View необходимо рассмотреть возможность изменения размера для удовлетворения своих ограничений. Например, если ваше приложение вызывает SetText() для объекта View , который переносит текст, возможно, потребуется изменить размер View .

Если подобные случаи занимают слишком много времени, они могут помешать рендерингу кадра в течение разрешенных 16 мс, что может привести к пропаданию кадров и замедлению анимации.

Поскольку вы не можете переместить эти операции в рабочий поток — ваше приложение должно обрабатывать их в основном потоке — лучше всего оптимизировать их так, чтобы они занимали как можно меньше времени.

Управление сложными макетами

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

Для сложной компоновки иногда стоимость возникает только при первом расчете компоновки. Например, когда ваше приложение перерабатывает сложный элемент списка в объекте RecyclerView , системе необходимо разместить все объекты. В другом примере тривиальные изменения могут распространяться вверх по цепочке к родительскому элементу, пока не достигнут объекта, который не влияет на размер родительского элемента.

Распространенная причина, по которой макет занимает много времени, заключается в том, что иерархии объектов View вложены друг в друга. Каждый вложенный объект макета увеличивает стоимость этапа макета. Чем более плоская иерархия, тем меньше времени потребуется для завершения этапа макета.

Мы рекомендуем использовать редактор макетов для создания ConstraintLayout вместо RelativeLayout или LinearLayout , поскольку это, как правило, более эффективно и уменьшает вложенность макетов. Однако для простых макетов, которые можно создать с помощью FrameLayout , мы рекомендуем использовать FrameLayout .

Если вы используете класс RelativeLayout , вы можете добиться того же эффекта с меньшими затратами, используя вместо этого вложенные невзвешенные представления LinearLayout . Однако если вы используете вложенные взвешенные представления LinearLayout , стоимость макета будет намного выше, поскольку для этого требуется несколько проходов макета, как описано в следующем разделе.

Мы также рекомендуем использовать RecyclerView вместо ListView , поскольку он может повторно использовать макеты отдельных элементов списка, что более эффективно и может улучшить производительность прокрутки.

Двойное налогообложение

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

Например, когда вы используете контейнер RelativeLayout , который позволяет позиционировать объекты View относительно позиций других объектов View , платформа выполняет следующую последовательность:

  1. Выполняет этап компоновки и измерения, во время которого платформа вычисляет положение и размер каждого дочернего объекта на основе запроса каждого дочернего объекта.
  2. Использует эти данные с учетом веса объектов, чтобы определить правильное положение коррелирующих представлений.
  3. Выполняет второй проход макета для окончательного определения положения объектов.
  4. Переход к следующему этапу процесса рендеринга.

Чем больше уровней имеет ваша иерархия представлений, тем больше вероятность снижения производительности.

Как упоминалось ранее, ConstraintLayout обычно более эффективен, чем другие макеты, за исключением FrameLayout . Он менее склонен к многократному проходу макета и во многих случаях устраняет необходимость вложения макетов.

Контейнеры, отличные от RelativeLayout также могут привести к увеличению двойного налогообложения. Например:

  • Представление LinearLayout может привести к двойному проходу компоновки и измерения, если вы сделаете его горизонтальным. Двойной проход макета и измерения также может произойти в вертикальной ориентации, если вы добавите measureWithLargestChild , и в этом случае платформе может потребоваться выполнить второй проход для определения правильных размеров объектов.
  • GridLayout также допускает относительное позиционирование, но обычно позволяет избежать двойного налогообложения за счет предварительной обработки позиционных отношений между дочерними представлениями. Однако если в макете используются веса или заливка с помощью класса Gravity , преимущества предварительной обработки теряются, и платформе, возможно, придется выполнить несколько проходов, если контейнер является RelativeLayout .

Несколько проходов компоновки и измерения не обязательно снижают производительность. Однако они могут стать обузой, если окажутся не в том месте. Будьте осторожны в ситуациях, когда к вашему контейнеру применимо одно из следующих условий:

  • Это корневой элемент в вашей иерархии представлений.
  • Под ним находится глубокая иерархия представлений.
  • Существует множество случаев, когда он заполняет экран, подобно дочерним элементам объекта ListView .

Диагностика проблем иерархии представлений

Производительность макета — сложная и многогранная проблема. Следующие инструменты помогут вам определить, где возникают узкие места в производительности. Некоторые инструменты предоставляют менее точную информацию, но могут дать полезные подсказки.

Перфетто

Perfetto — это инструмент, предоставляющий данные о производительности. Вы можете открыть трассировки Android в пользовательском интерфейсе Perfetto .

Рендеринг профиля на графическом процессоре

Встроенный в устройство инструмент рендеринга Profile GPU , доступный на устройствах под управлением Android 6.0 (уровень API 23) и более поздних версий, может предоставить вам конкретную информацию об узких местах производительности. Этот инструмент позволяет увидеть, сколько времени занимает этап компоновки и измерения для каждого кадра рендеринга . Эти данные могут помочь вам диагностировать проблемы с производительностью во время выполнения и определить, какие проблемы с макетом и измерениями необходимо устранить.

В графическом представлении собираемых данных рендеринг Profile GPU использует синий цвет для обозначения времени компоновки. Дополнительные сведения о том, как использовать этот инструмент, см. в разделе «Профиль скорости рендеринга графического процессора» .

Ворс

Инструмент Lint в Android Studio может помочь вам почувствовать неэффективность иерархии представлений. Чтобы использовать этот инструмент, выберите «Анализ» > «Проверить код» , как показано на рисунке 1.

Рисунок 1. Выберите «Проверить код» в Android Studio.

Информация о различных элементах макета отображается в разделе Android > Lint > Performance . Чтобы просмотреть более подробную информацию, щелкните каждый элемент, чтобы развернуть его и отобразить дополнительную информацию на панели в правой части экрана. На рис. 2 показан пример расширенной информации.

Рисунок 2. Просмотр информации о конкретных проблемах, выявленных инструментом Lint.

Щелкнув элемент, вы увидите проблемы, связанные с этим элементом, на панели справа.

Чтобы узнать больше о конкретных темах и проблемах в этой области, см. документацию Lint .

Инспектор макетов

Инструмент «Инспектор макетов» Android Studio обеспечивает визуальное представление иерархии представлений вашего приложения. Это хороший способ навигации по иерархии вашего приложения, обеспечивающий четкое визуальное представление родительской цепочки конкретного представления и позволяющий проверять макеты, создаваемые вашим приложением.

Представления, которые представляет Layout Inspector, также могут помочь выявить проблемы с производительностью, возникающие из-за двойного налогообложения. Он также может предоставить вам возможность идентифицировать глубокие цепочки вложенных макетов или области макета с большим количеством вложенных дочерних элементов, что может быть источником снижения производительности. В этих случаях этапы компоновки и измерения могут оказаться дорогостоящими и привести к проблемам с производительностью.

Дополнительные сведения см. в разделе Отладка макета с помощью Layout Inspector и Layout Validation .

Решение проблем с иерархией представлений

Фундаментальная концепция решения проблем производительности, возникающих из-за иерархий представлений, на практике может оказаться сложной. Чтобы предотвратить наложение штрафов за производительность со стороны иерархий представлений, необходимо сгладить иерархию представлений и уменьшить двойное налогообложение. В этом разделе обсуждаются стратегии достижения этих целей.

Удалите лишние вложенные макеты

ConstraintLayout — это библиотека Jetpack с большим количеством различных механизмов позиционирования представлений внутри макета. Это уменьшает необходимость вложения одного ConstaintLayout и может помочь сгладить иерархию представлений. Обычно с помощью ConstraintLayout выравнивать иерархии проще, чем с другими типами макетов.

Разработчики часто используют больше вложенных макетов, чем необходимо. Например, контейнер RelativeLayout может содержать один дочерний элемент, который также является контейнером RelativeLayout . Такое вложение является избыточным и добавляет ненужные затраты в иерархию представлений. Lint может указать вам на эту проблему, сокращая время отладки.

Принять слияние или включить

Частой причиной избыточных вложенных макетов является тег <include> . Например, вы можете определить повторно используемый макет следующим образом:

<LinearLayout>
    <!-- some stuff here -->
</LinearLayout>

Затем вы можете добавить тег <include> , чтобы добавить в родительский контейнер следующий элемент:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/app_bg"
    android:gravity="center_horizontal">

    <include layout="@layout/titlebar"/>

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="@string/hello"
              android:padding="10dp" />

    ...

</LinearLayout>

Предыдущее включение излишне вкладывает первый макет во второй макет.

Тег <merge> может помочь предотвратить эту проблему. Дополнительные сведения об этом теге см. в разделе Использование тега <merge> .

Примите более дешевую планировку

Возможно, у вас не получится настроить существующую схему макета, чтобы она не содержала избыточных макетов. В некоторых случаях единственным решением может быть выравнивание иерархии путем переключения на совершенно другой тип макета.

Например, вы можете обнаружить, что TableLayout предоставляет ту же функциональность, что и более сложный макет со многими позиционными зависимостями. Библиотека Jetpack ConstraintLayout предоставляет аналогичные RelativeLayout функциональные возможности, а также дополнительные функции, помогающие создавать более плоские и эффективные макеты.