Оптимизация списков в мобильном приложении

This post is also available in: Английский

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

Достаточно подробно оптимизация элементов отображения в мобильном приложении рассматривается в видеоролике Performance-Tuning Mobile Flex Applications

Вполне естественно, что разработчики FLEX-фреймворка стремятся максимально использовать его возможности; однако часть советов из этого ролика может поставить нас в определенные рамки. Если выйти за пределы этих рамок, можно оптимизировать список еще больше. Так, отказ от использования MXML при реализации элементов списка и использование вместо этого чистого ActionScript может дать значительный прирост производительности. К сожалению, такой отказ от MXML может негативно сказаться на времени разработки приложения.

Два главных критерия, за которые приходится «сражаться», — это время первоначальной инициализации конкретного компонента и FPS — количество кадров в секунду при работе пользователя со списком (т.е. при скроллинге).

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

  1. Использовать оптимизированный IconItemRenderer там, где это возможно. Разработчики FLEX-фреймворка постарались максимально оптимизировать базовый элемент списка. Если предполагается использование списков, отображающих последовательность элементов иконка/текст, то вполне можно использовать IconItemRenderer;
  2. Использовать opaqueBackground для элемента списка. Это позволит гарантировать FLASH-плееру, что в элементе списка не будет прозрачных областей, которые способны существенно понизить FPS. Но, как показывает опыт, даже если предоставлен дизайн с использованием прозрачных или полупрозрачных элементов списка, можно добиться неплохих результатов по FPS даже не указывая opaqueBackground. Например, в приложениях Chamions League и Europe League мы не использовали opaqueBackground, так как элементы списка по задумке дизайнера должны были быть полупрозрачными;
  3. Использовать ContentCache для повторяющихся изображений в списке;
  4. Использовать cacheAsBitmap для элемента списка. Естественно, это тоже нужно делать разумно. Если предполагается анимация в элементах списка, то нужно либо отказаться от cacheAsBitmap, либо отключать это кэширование на время анимации;
  5. Не использовать групп и размеров в процентах;
  6. Не использовать MXML для элементов списка вообще. Использовать только ActionScript. От этого очень сильно зависит время первоначальной инициализации;
  7. Не использовать элементы RichText;
  8. Для текстовых полей использовать только TextField: отказаться не только от Label, но даже от StyleableTextField. При использовании StyleableTextField вместо TextField время первоначальной инициализации может быть в 1,5 раза дольше. Назначение стилей занимает больше времени, чем назначение TextFormat. К тому же, в случае использования StyleableTextField обязательно указание стилей fontAntiAliasType:normal; fontGridFitType:none; иначе FLEX при назначении стилей внутри фреймворка выдастся исключение, что нежелательно;
  9. Не задавать для текстовых полей TextField параметр autoSize. Как это ни странно, это крайне негативно влияет на FPS. Если нужно текстовое поле, которое будет автоматически подстраиваться под размер текста, то нужно создать класс, наследующийся от TextField и переопределяющий public function set text(value:String):void, где и будет задаваться размер текстового поля. Как правило, все текстовые поля внутри элементов списка — однострочные, поэтому определение размера тестового поля при назначении текста не должно вызвать проблем;
  10. В блоке graphics элемента списка отрисовывать фон, разделители и другие элементы, одинаковые для всех элементов списка. Как известно, с растровым форматом Flash-плеер умеет работать очень быстро, поэтому на время первоначальной инициализации это практически не влияет, а вот на FPS это может повлиять существенно, причем в лучшую сторону. Мы пробовали отрисовывать также и текстовые поля в graphics элемента списка, используя одно единственное статическое текстовое поле (которое не добавлялось в список отображения); это не дало никаких преимуществ в плане увеличения производительности, но и хуже от этого не стало. Эти варианты примерно сопоставимы по времени, но при использовании для отрисовки статического текстового поля сильно возрастает сложность кода и он становится менее понятным;
  11. Использовать растровые картинки в масштабе 1:1. Масштабирование bitmap-изображений на лету и их отрисовка могут отрицательно скзазаться на FPS;
  12. Параметр useVirtualLayout для списка – тут все очень неоднозначно. Если мы выставляем данный параметр в true, то время первоначальной инициализации будет очень мало и список появится на экране практически мгновенно, поскольку инициализируется и отрисовывается только нужное количество элементов списка, присутствующих на экране. Но при работе пользователя со списком могут наблюдаться лаги и подтормаживания, т.к. элементы списка пересоздаются на лету или происходит переназначение данных старым элементам списка. В то же время, если выставить useVirtualLayout=”false”, то при большом количестве элементов список будет создаваться заметно дольше, однако после создания, при условии правильной оптимизации, визуально список будет работать очень быстро. Поэтому можно порекомендовать следующее: по возможности использовать useVirtualLayout=”false”, но если элементы списка все равно достаточно тяжелые, то либо организовывать постраничное отображение элементов в списке, либо, все-таки, выставлять useVirtualLayout=”true”;
  13. Стараться использовать поменьше FLEX-компонентов (к сожалению, это так). А если использовать их, то обязательно проверять на время первоначальной инициализации с этим компонентом и без него. Например, использование одного единственного ToggleButton в элементе списка увеличивало время первоначальной инициализации в два раза!
  14. Использовать BitmapImage вместо Image. Это позволяет уменьшить время первоначальной инициализации.

Для того, чтобы узнать время первоначальной инициализации списка, нужно замерить время между началом назначения данных и последующим наступлением события FlexEvent.UPDATE_COMPLETE для списка. Естественно, чем больше будет элементов в списке, тем более точные данные можно получить, разделив время инициализации на общее количество элементов в списке. Посмотреть FPS можно, например, если добавить на Stage компонент Stats.

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