Соображения на тему производительности и удобства использования (User Experience) в успешном PhoneGap приложении

Мне кажется одним из самых распространенных вопросов, который задают люди, разрабатывающие приложения с помощью PhoneGap'а, является вопрос производительности. Как я могу заставить мое приложение работать быстрее? Как я могу сделать его похожим на нативное? Какие приемы помогут моему приложению чувствовать себя как дома на определенной платформе? Как достигнуть в приложении нативного вида и поведения? В этом посте я постараюсь пролить свет на некоторые техники, способствующие созданию отличных приложений на PhoneGap'е, а также постараюсь развенчать некоторые мифы, витающие вокруг "интервебов" (interweb - приложения на мобильных устройствах, созданные с помощью вебтехнологий)...

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

Когда мы говорим о производительности, на самом деле мы говорим о воспринимаемой производительности и отзывчивости приложениия с точки зрения конечного пользователя. Так как приложения, написанные с помощью PhoneGap'а основаны на HTML и веб браузере, вы ограничены производительностью браузера на конкретной платформе. Тем не менее, это не значит, что все приложения, основанные на HTML, медленные. Есть богатое множество успешных приложений с интерфейсом основанном на HTML. Некоторые даже были "фичерены" в апсторах. Мы не можем сделать веб браузер быстрее, но мы можем помочь вам сделать взаимодействие с HTML вашего приложения внутри веб браузера более быстрым.

Так или иначе, если быстродействие веб браузера не удовлетворяет вашим запросам, не забывайте про возможность использовать PhoneGap как подвид (subview) в нативном приложении. В этом случае вы можете использовать элементы нативного пользовательского интерфейса там, где они вам нужны, и интерфейс на PhoneGap'овском HTML/JS там, где это необходимо. Далее, вы можете использовать PhoneGap'овский мост native-to-JS, для управления двусторонней коммуникацией нативного кода и джаваскрипта внутри компонента веб браузера. Об этом чуть позже...

HTML/Браузерное Быстродействие

Во-первых, давайте поговорим о специфике мобильного HTML- и веб-быстродействия. Здесь есть множество приёмчиков, некоторые из них чрезвычайно верные, но некоторые только вводят в заблуждение. Позвольте мне расставить некоторые вещи по своим местам...

Аппаратное Ускорение

По всему вебу можно найти общие положения, рекомендующие использовать аппаратное ускорение везде, где это возможно, в каждом скрипте, всего лишь добавив css стиль transform: translate3d:

transform: translate3d(0,0,0);

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

По ряду причин вы должны быть очень избирательны, когда используете translate3d для форсирования аппаратного ускорения.

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

Во-вторых, использование translate3d сопровождается загрузкой всего контента DOM-элементов в память GPU для рендеринга. В большинстве случаев время загрузки в память GPU незначительно и резутаты подобных манипуляций положительны. Однако, если вы применяете translate3d на очень большое и сложное дерево DOM-элементов, может появиться заметная задержка, даже на самых новых и свежих устройствах и операционных системах. Чем больше DOM-дерево, тем больше задержка. Когда это случается, интерфейс браузера может просто зависнуть на это время. Самая долгая задержка, с которой я сталкивался, была меньше секунды, тем не менее, когда ваш интерфейс зависает хотя бы на полсекунд, это оказывает глубоко негативное впечатление. Вдобавок, всегда, когда вы вносите изменения в DOM-элементы (неважно, контент это или стиль), текстура графического интерфейса перезагружается в память GPU.

В-третьих, избегайте вложения DOM-элементов, которые используют translate3d. Давайте представим ситуацию, когда у вас множество DOM-элементов внутри

элементов, многочисленные уровни вложенности, и все эти элементы, включая самый внешний
используют translate3d для рендеринга графическим ускорителем. Для того, чтобы внешний
был отрисован, необходимо, чтобы все его дети были отрисованы. Для того чтобы все эти дети были отрисованы, все их дети также должны быть отрисованы, и т. д... Это означает, что все эти элементы-потомки должны быть загруженны в GPU до того, как родительский элемент сможет отрендериться. Это всё накапливается очень быстро. Вложенные элементы, которые используют этот хак со стилем translate3d потребуют больше времени для загрузки, и могут занять значительно больше памяти, приводя к весьма низкой производительности, медленным загрузкам в GPU, или вообще к падениям приложения (см. параграфы выше).

В-четвертых, определите максимально допустимый размер текстуры для GPU. Если DOM-элемент больше максимально допустимого размера текстуры, поддерживаемого графическим ускорителем (как по ширине, так и по высоте), вы столкнетесь с графическими артефактами и низкой производительностью. Вы когда-нибудь видели, как DOM-элементы использующие translate3d мерцают во время анимации или скроллинга? Если да, высока вероятность того, что размеры DOM-элементов больше, чем размеры текстуры, поддерживаемой графическим ускорителем мобильного устройства. Максимальный размер текстуры на множестве девайсов составляет 1024х1024, но разнится от платформы к платформе. Максимальный размер текстуры на iPad 2 составляет 2048x2048, а на iPad 3/4 - 4096x4096. Если максимальный размер текстуры на устройстве составляет 1024, а высота вашего DOM-элемента составляет 1025 пикселей, вы столкнетесь с досадным мерцанием и снижением производительности.

Замечание: На некоторых платформах мерцание элементов также может быть связано с реализацией браузера, это можно исправить установкой css свойства backface-visibility. Подробней здесь.

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

Translate3d однозначно может принести положительные результаты, но в то же время это может привести и к негативным результатам, всё зависит от того, как этот приём использован.  Используйте его с умом и всегда, всегда, всегда тестируйте на мобильных устройствах.

Влияние Контентного Reflow

Если вы никогда не слышали о reflow раньше, вам стоит прочитать эту часть внимательней. Reflow - это браузерный процесс вычисления позиций и геометрии DOM-элементов. Это включает в себя вычисление ширины и высоты, обертки текста, flow/relative позиции DOM-элементов, и всего, что с этим связано.

Расчеты, производимые процессом reflow, могут быть весьма затратными, и вам стоит минимизировать стоимость и количество контентного reflow, для достижения оптимального быстродействия. Если вы когда-нибудь анимировали ширину DOM-элемента и видели, как фрэймрейт падает до 5 кадров в секунду или ниже, - вы видели процесс reflow в действии.

Процесс reflow запускается всякий раз, когда изменяется DOM-контент, изменяются размеры DOM-элементов, позиции, отступы, размеры полей и т. д. На настольных компьютерах вы можете не чувствовать влияние процесса reflow, но на мобильных устройствах это может оказать значительное влияние на производительность. Возможно вам не удастся избавиться от всех reflow процессов (ну, если только ваш контент абсолютно никогда не изменяется), но вы можете снизить воздействие этих reflow процессов на быстродействие приложения.

Если у вас есть желание узнать о reflow процессах побольше, я вам настоятельно порекомендую прочесть это:

Если вы видели списки советов для PhoneGap и Мобильных веб-приложений, вы наверняка заметили высказывания вроде "уменьшите количество DOM-узлов", "избегайте глубоко вложенного html", используйте css-анимацию, подгружайте изображения заранее, и т. д. Причины возникновения подобных советов в том, что они помогают минимизировать влияние и частоту reflow-операций.

  • Снижайте количество DOM-сущностей – Меньше DOM-узлов - меньше количество нод, которые будут измерены и подсчитаны во время reflow-операций.
  • Избегайте глубоко вложенную HTML DOM структуру – Глубже HTML структура - сложней и дороже становятся reflow-операции. В добавок, изменение самого нижнего уровня вложенности приведет к запуску reflow-операций вверх по всему дереву, и в итоге к возросшей стоимости расчетов.
  • Используйте CSS transform – В дополнение к активации аппаратного ускорения, описанного выше, вы можете изменять DOM-элементы без вызова reflow-процесса, если эти изменения производятся с помощью CSS3 transform стилей. Это и translation по осям X, Y, и Z, и масштабирование и вращение. Тем не менее, будьте осторожны и не переборщите с translate3d, по причинам, описанным выше.
  • Используйте CSS анимацию и перемещения - CSS анимация и транзишены могут всё значительно ускорить. Тем не менее это не панацея от всех проблем. Если вы используете CSS транзишены с параметрами, которые могут запустить процесс reflow (например изменение ширины и высоты), производительность вашего приложения может снизиться. Вы можете добиться плавной анимации используя CSS транзишены в совокупности с CSS трансформами (как описано выше), потому что они изменяют внешний вид, но не запускают reflow-процесс.
  • Используйте фиксированные ширину и высоту DOM-элементов - Если вы не изменяете размер контента, не запускается reflow-процесс. Это относится и к div-ам (или другим элементам), это же применимо и к загрузке изображений. Если вы не указываете размер изображения заранее и ждете, пока изображение загрузится, в процессе загрузки изображения выполняются ресурсоёмкие reflow-операции. А если у вас грузится несколько изображений, ресурсов затрачивается ещё больше.
  • Подгружайте изображения или ресурсы с использованием CSS стилей – Здесь двойная выгода. Во-первых, ваши изображения будут уже на готове в момент, когда они вам понадобятся. Это убирает задержку или мерцание, во время ожидания контента. Во-вторых, если у вас уже есть изображения или другие ресурсы, подгруженные в память перед тем, как они понадобились, значит reflow-процесс не запустится повторно, в момент когда эти изображения/ресурсы будут использованы (первый reflow произойдет при первой загрузке DOM контента, второй произойдет при пересчете во время загрузки изображения/ресурсов).
  • Be smart with your HTML DOM elements – Let’s say you want to create and populate a element based on data you have in a JavaScript array. Слишком ресурсоёмким будет сначала добавить
    элемент, затем в цикле обойти массив и на каждом шаге добавлять по строке к существующей DOM-структуре. Гораздо лучше будет циклом обойти массив, сразу создать и заполнить DOM-элемент таблицы. Затем, когда цикл отработает, добавить
    к существующей DOM-структуре. Каждый раз, когда вы добавляете элемент в DOM-структуру, вызывается reflow-процесс. Необходимо минимизировать количество подобных вызовов.

    В целом, вам нужно минимизировать количество работы, проделываемой браузером для рассчета макета и позиционирования DOM-элементов.

    Сохраняйте Графику Простой

    Дизайнеры обожают делать великолепно выглядящие макеты, но не всегда подобный подход приводит к хорошей производительности. Чрезмерное использование CSS теней и градиентов может ухудшить производительность приложения на различных платформах и реализациях браузеров. Я не запрещаю использовать их все, просто используйте их с умом. Например, при использовании CSS градиентов и теней производительность приложения на Андроидах ниже чем на iOS, что связано с реализацией веб-вью в операционной системе.

    Тач Интерактивность

    Вы наверняка слышали до этого, что "мышиные" события медленные, а тач события быстрые. Это абсолютно верное утверждение. Вместо использования событий "mousedown”, “mousemove”, “mouseup”, или “click” стоит использовать “touchstart”, “touchmove” и “touchend”.

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

    You may have noticed that there is no equivalent of “click” for touch events… Всё верно, нет ни единого. Тем не менее, вы можете управлять тач событиями вручную, для имитации клика, либо использовать существующую библиотеку с реализованными тапами и жестами. Вот несколько вариантов: Zepto, FastClick, Hammer.js, iScroll (да, iScroll убирает задержку у клика внутри прокручиваемой области)

    Оптимизация JavaScript Кода

    Пишите эффективный код, который не блокирует UI поток. Я могу долго говорить об этом, но вместо этого просто посоветую использовать оптимизацию и best practices. Вот несколько статей на эту тему:

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

    Нативная Производительность

    Итак, давайте представим, что вы хотите достичь нативного опыта взаимодействия (UX), нативной навигации между контейнерами, но вам также хочется создавать контент легко и просто с помощью HTML. Никто не говорит, что вы не можете использовать PhoneGap как вложенный вид (subview) в нативном приложении. Фактически, ести несколько ОЧЕНЬ широко используемых приложений, в которых была использована эта техника... Если бы я только мог вам рассказать (соглашение о неразглашении).

    Этот подход требует опыта нативной разработки, но вы можете использовать CordovaView в кажестве вложенного вида в нативном приложении как на iOS, так и на Android. Это даст вам возможность использовать преимущества нативных компонентов, и PhoneGap/HTML интерфейса когда вам это нужно. PhoneGap предоставляет мост от нативного к JS, для полной двусторонней коммуникации между нативным и JS слоем.

    Когда используете HTML, очень легко создавать собственный интерфейс, неважно сложные ли это графики, табличные данные или что-то еще. С использованием CordovaView вы заставляете каждый слой (нативный/HTML) делать то, что он должен делать.

    Вы можете узнать больше об использовании CordoveView внутри нативного приложения из PhoneGap Документации.

    Соображения о UI и UX

    Мои слова могут показаться слишком общими, но: вам стоит обращать внимание на создание качественного пользовательского опыта взаимодействия (UX). Если вы не сфокусируетесь на качестве, ваше приложение будет страдать. Я уже рассказывал о пользовательском интерфейсе и опыте взаимодействия в деталях, тем не менее обозначу один частый вопрос, который мне задают: "Как мне сделать приложение, которое выглядит нативным на всех платформах?"

    Мой ответ: Никак.

    Позвольте пояснить этот момент: я не говорю вам перестать делать что-либо, что чувствует себя как дома на какой-либо платформе. Я предлагаю вам не пытаться достигнуть идеального ощущения нативности в мельчайших деталях. Главная причина в том, что 1) этого очень сложно добиться и 2) если что-то изменится в операционной системе, станет очевидным, что ваше приложение не является нативным, но косит под него.

    Вы наверное слышали, а может и не слышали об эффекте “зловещей долины” (uncanny valley). Применимо к PhoneGap приложениям, если вы подбираетесь очень, очень близко к поведению практически идеально повторяющему нативные приложения, но есть незначительные отличия, это станет сигналом, что "что-то не так", "что-то неправильно" с вашим приложением, и может вызвать незаслуженно негативный опыт у пользователей.

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

    Так или иначе, если вы хотите подражать нативным приложениям, этого можно достигнуть всецело используя CSS стили.

    Тестируйте На Устройствах

    Я не могу достаточно акцентировать ваше внимание на этом моменте.  Всегда тестируйте на реальных устройствах. Всегда. Я люблю тестировать на старых девайсах, потому что если ты можешь заставить свое приложение работать быстро на старом устройстве, оно будет еще быстрей на новом. Обязательно тестируйте на устройствах с целевыми операционными системами. Для iOS я тестирую на iPhone4 (не 4s) и на iPad2. На андроиде я использую Motorola Atrix, планшет Nexus 7, Kindle Fire (первое поколение), и Samsung Galaxy 10.1 (первое поколение). Я тестирую и на других платформах, как только появляется возможность.  Да что уж там, я даже в магазины захожу лишь для того, чтобы установить приложение на одно из стендовых устройств, чтобы посмотреть как оно работает.

    • http://www.rootinfosol.co.uk Nikhil

      Gr8 Post Andrew. Thanks a Bunch.

      Wat should be the approach/Best Practices for getting scrolling to work on device between Android 2.2 to Android 4.0.0

      I know from Android 4.0, the scroll event and Hardware acceleration is very helpful, but for devices having a lower version of OS, becomes difficult.

      Thanks!!!
      Nikhil Juneja

      • http://www.tricedesigns.com Andrew

        It depends what you’re tring to build. I’ve been successful with iScroll on those devices. It’s not quite as fluid as native scrolling, but does the job well, especially if you have multiple scrollable areas (for example on a tablet). You can also use CSS fixed positioning and native scrolling (granted position:fixed only has partial support, so you have to test everything).

    • Trevor

      FastClick was giving me unpredictable results when doing fast taps or lazy thumb taps. I ultimately decided to remove this because client was complaining that buttons were sometimes not working.. it was much faster tho, just not at production quality in my opinion

      • https://twitter.com/SergiKoles Sergi

        Had the same problem even with optimized jqMobi framework — lazy taps didn’t always result in onTap event.

        I’ve switched to Adobe Air with Starling/Feathers UI you can create extremely responsive apps with almost same cross-platform coverage as PhoneGap (excluding Symbian:).

    • http://jonathanstark.com Jonathan Stark

      Excellent post! Too many great tips to pick just on. Keep up the great work :)

    • http://jonathanstark.com Jonathan Stark

      Excellent post! Too many great tips to pick just one. Keep up the great work :)

    • Anil Yanamandra

      We learnt a lot of this after a lot of “trial and error” research. Happy to see a post that neatly summarizes it all.

      Thanks!

    • http://twitter.com/freddywang Fred

      http://www.tricedesigns.com/2012/01/17/mobile-web-phonegap-html-dev-tips/

      The old post was recommending usage of translate3d to force hardware acceleration. I guess you need to amend the post to indicate some considerations of rendering performance.

      Translate3D is actually okay. It was initially introduced from the iOS universe. But then Android kicks in. With many Android devices came without hardware accelerated gpu integration, things get murky.

      • http://www.tricedesigns.com Andrew

        I’m not saying don’t use it, just use it sparingly and deliberately… but yes, I should update that post. I still highly reccommend using translate3d to improve performance, but abusing it can cause problems.

        • http://twitter.com/freddywang Fred

          Yeah me too, I am not saying not to use it. It will be nice to weigh in an update for some rendering performance considerations.

    • Anoop Kumar

      Great post … Really industry relavant mweb is discussed.

    • http://monaca.mobi/ Masahiro Tanaka

      Great article, very much worth reading. To share this great post to non-English people, just to mention I have written Japanese translation of this article and post it here: http://blog.asial.co.jp/1142.

    • https://twitter.com/SergiKoles Sergi

      Andrew,

      Have you used Adobe Air with Starling/Feathers UI? With Stage3d acceleration it’s very powerful combination that allows devs like me to create apps beyond games.

      • http://www.tricedesigns.com Andrew

        Hi Sergi,
        I have used it, and you can get some great performance out of it. However, my primary focus is on HTML/js, web standards & PhoneGap, so I’m focusing on those topics for my blog. Starling/feathers looks great

    • Frederico Galvão

      It is indeed very good to see such posts summarizing great tips on this topic.
      I could suggest some I have found on the way to solve some of the problems here:
      – Toe.js -> for the fast “tap” and similar events, for those who use jQuery, in a very simple and unobtrusive way.
      – Overthrow.js -> for the scroll on browsers that do not support overflow:auto and native scrolling (aka Androids for the management of views and elements before messing with the DOM in a structured way.
      – *jqMobi* -> for those who can still afford the change from jQuery to jqMobi, as far as I’ve read about it as an alternative, it seems like one of the best choices at the moment.

      I’ve yet to see some deep analysis of the transform3d impact on memory and scrolling in edge cases, though.

    • Pingback: “PhoneGap Legends” – A Sample Game App | ANDREW TRICE()

    • Pingback: Using Creative Cloud and CSS Translate3D to Create an 8 Way Character Animation - Renaun Erickson | Renaun Erickson()

    • Mike

      A note about testing on devices: If you are building for Android, it’s great to test on an older Gingerbread phone and on tablets, but note that tablets, because of their larger dimensions, will mask certain problems that smaller screens will expose.

      Also, surprisingly, the worst phone for a recent app development was the Galaxy sIII running Jelly Bean. Things that worked great on old Gingerbread phones and smaller Jelly Bean phones were buggy on the s3 like nowhere else. You have to test on as many devices as you can get your hands on for Android.

      At first, when developing, Android seems like a cakewalk because it’s so much easier to allow anyone to test compared to iPhone. But then, as time goes on and you have unique bugs device after device, you start to appreciate the narrow range of phones and the concentration in the two latest OS versions with Apple iOS.

    • @jaimoto81

      Nice Post, really helpful and it is nice to understand which things could affect the performance of a Phonegap App!

      However, you recommend to Use CSS transforms but “scale” does not work properly, links are misplaced and not working correctly

    • Davy M

      Great Article . Lots of relevant stuff in there for a first time Phone Gap User.
      Many Thanks

    • Andreas Linnert

      Hi Sergi,
      can you tell me if it’s necessary to install AIR on Android devices in order to run AIR applications on those devices? I searched for examples of AIR applications and they run on my Android phone just like that – without AIR being installed. I’m trying to find out if it’s a good alternative to PhoneGap but I still don’t know how exactly it works.

      • http://twitter.com/sergikoles Sergi Kolesnik

        Andreas, these days (past 6 months) AIR runtime is packaged as part of the app, so there’s no longer a requirement for Android user to have AIR app on device.

    • ReyMaxwin

      Can we have a plus one button please :) great article.

    • Joe Weirather

      This is the best paragraph of advice I’ve read all week:

      “Instead, I recommend creating a unique identity for your app (that still meets App Store guidelines). Сюда входят внешний вид, стили кнопок, навигация, контент и так далее... Rather than mimicking every aspect of the native UX, build a cohesive experience around your identity or brand, and carry that identity through all of your mobile apps. People will associate this identity with your app, rather than comparing it to the native OS.”

      Well said.

      JPW

    • http://www.mobilepundits.com/ alva christi

      Great blog about phonegap app performance.There are many benefits of phonegap such as Mobile application built on PhoneGap development platform, run on multiple devices on same code base.The concept of “code once and deploy on any mobile device” cuts the development cost to drastically reduce the total cost.

    • Loïc Goyet

      Very great post !
      still relevant 2 years later ;)

    © 2015 Andrew Trice1