Оптимизация иерархий макетов

Попробуйте Compose
Jetpack Compose – это рекомендуемый набор инструментов для создания пользовательского интерфейса в Android. Узнайте, как работать с макетами в Compose.

Распространенное заблуждение, что использование базовых структур макета приводит к наиболее эффективным макетам. Однако каждый виджет и макет, который вы добавляете в приложение, требует инициализации, макета и рисования. Например, использование вложенных экземпляров LinearLayout может привести к чрезмерно глубокой иерархии View. Кроме того, вложение нескольких экземпляров LinearLayout, использующих параметр layout_weight, может быть особенно ресурсоемким, поскольку каждый дочерний элемент необходимо измерить дважды. Это особенно важно, когда макет раздувается повторно, например при использовании в RecyclerView.

В этой статье рассказывается, как использовать инспектор макетов и lint для проверки и оптимизации макета.

Проверьте макет

В Android SDK Tools есть инструмент Инспектор макетов, который позволяет анализировать макет во время работы приложения. Использование этого инструмента поможет вам обнаружить неэффективность в производительности макета.

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

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

Изображение, на котором показан один элемент в списке: одно изображение и два вертикально выровненных текста
Рисунок 1. Концептуальный макет для элемента в RecyclerView.

Инспектор макетов показывает список доступных устройств и их запущенных компонентов. Выберите компонент на вкладке Windows (Окна) и нажмите Layout Inspector (Инспектор макетов), чтобы посмотреть иерархию макета выбранного компонента. Например, на рисунке 2 показан макет для элемента списка, изображенного на рисунке 1.

Изображение, на котором показан инспектор макетов и композиция LinearLayout
Рисунок 2. Иерархия макета, показанного на рисунке 1, с использованием вложенных экземпляров LinearLayout.

Измените макет

Поскольку производительность предыдущего макета снижается из-за вложенного элемента LinearLayout, вы можете повысить производительность, выровняв макет, то есть сделав его неглубоким и широким, а не узким и глубоким. Для этого в качестве корневого узла можно использовать ConstraintLayout. Если вы преобразуете этот дизайн для использования ConstraintLayout, макет станет двухуровневой иерархией:

    <androidx.constraintlayout.widget.ConstraintLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      android:id="@+id/root"
      android:layout_width="match_parent"
      android:layout_height="52dp"
      android:background="#e4e6e4"
      android:padding="4dp">

      <ImageView
          android:id="@+id/image"
          android:layout_width="48dp"
          android:layout_height="48dp"
          android:background="#5c5c74"
          android:contentDescription="An example box"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toTopOf="parent" />

      <TextView
          android:id="@+id/title"
          android:layout_width="0dp"
          android:layout_height="0dp"
          android:layout_marginStart="4dp"
          android:background="#745c74"
          app:layout_constraintBottom_toTopOf="@+id/subtitle"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toEndOf="@id/image"
          app:layout_constraintTop_toTopOf="parent" />

      <TextView
          android:id="@+id/subtitle"
          android:layout_width="0dp"
          android:layout_height="0dp"
          android:background="#7e8d6e"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toStartOf="@id/title"
          app:layout_constraintTop_toBottomOf="@+id/title" />
  </androidx.constraintlayout.widget.ConstraintLayout>
    

Проверка нового макета выглядит следующим образом:

Изображение, на котором показан 3D-инспектор макетов
Рисунок 3. 3D-режим инспектора макетов.

Преимущества этого умножаются, поскольку этот макет используется для каждого элемента в списке.

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

В некоторых сложных макетах система может тратить ресурсы на измерение одного и того же элемента пользовательского интерфейса более одного раза. Это явление называется двойным налогообложением. Дополнительную информацию о двойном налогообложении и способах его предотвращения можно найти в разделе Производительность и иерархии представлений.

Использовать lint

Рекомендуем запустить инструмент lint для файлов макета, чтобы найти возможные оптимизации иерархии View. Lint заменяет инструмент layoutopt и обладает более широкими функциональными возможностями. Ниже приведены примеры правил линтинга.

  • Используйте составные графические объекты. Вы можете более эффективно обрабатывать LinearLayout, содержащий ImageView и TextView, как составной графический объект.
  • Объединить корневой фрейм. Если корневым элементом макета является FrameLayout, который не предоставляет фон или отступы, вы можете заменить его тегом merge, который немного эффективнее.
  • Удалите ненужные листья. Вы можете удалить макет, у которого нет дочерних элементов или фона (поскольку он невидим), чтобы сделать иерархию макетов более плоской и эффективной.
  • Удалите ненужные родительские элементы. Вы можете удалить макет с дочерним элементом, у которого нет элементов того же уровня, который не является макетом ScrollView или корневым макетом и у которого нет фона. Вы также можете переместить дочернее представление непосредственно в родительское, чтобы получить более плоскую и эффективную иерархию макета.
  • Избегайте глубоких макетов. Макеты со слишком большим количеством вложений плохо влияют на производительность. Чтобы повысить производительность, используйте более плоские макеты, например ConstraintLayout. По умолчанию максимальная глубина для проверок lint равна 10.

Ещё одно преимущество инструмента lint – его интеграция с Android Studio. Lint автоматически запускается при каждой компиляции программы. В Android Studio вы также можете запустить проверку lint для определенного варианта сборки или для всех вариантов сборки.

Вы также можете управлять профилями проверок и настраивать проверки в Android Studio, выбрав File > Settings > Project Settings (Файл > Настройки > Настройки проекта). Откроется страница "Конфигурация проверки" со списком поддерживаемых проверок:

Изображение, на котором показано меню проверок в Android Studio
Рисунок 4. странице конфигурации проверки.

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

Подробнее о макетах и ресурсах макетов…