По работе мне неоднократно приходилось сталкиваться с выбором подходящей технологии для мобильной разработки. Ниже я попробовал собрать и классифицировать основные фреймворки по используемым подходам, достоинствам и недостаткам. Если какая-то моя информация некорректна, или устарела — комментарии приветствуются. Любой кросс-платформенный фреймворк — это слой абстракции над нативной платформой и позволяет обращаться только к тем её возможностям, которые прямо поддерживаются фреймворком. В большинстве случаев есть возможность расширить поддержку возможностей платформы путём написания нативных плагинов к фреймворку, но в некоторых случаях это может существенно усложнить разработку. Свежий пример из нашумевшей статьи AirBnb — React Native, который в данный момент не умеет “из коробки” работать с 64-битными Android-библиотеками. Также нужно обратить внимание, что нативные плагины и основной код кросс-платформенного приложения, как правило, исполняются в разных процессах и взаимодействие между ними может вызывать проблемы с производительностью. Для работы с датчиками, или SQLite это, обычно, не проблема, но если вы используете, скажем, библиотеку OpenCV в качестве нативного плагина и начинаете кидать видео между ней и основным приложением, замедление может быть существенным. Во-первых, само наличие разработчиков зависит от распространённости фреймворка. Найти людей под React Native может быть даже легче, чем нативных разработчиков, а, например, с Flutter намного сложнее. Насколько нужно учитывать этот фактор — зависит от задач. Большинство стартапов могут не обращать на него внимания, поскольку изучение новой технологии является скорее бонусом для действующих и потенциальных сотрудников. С другой стороны, крупный бизнес вынужден учитывать рынок труда. Считается, что вероятность того, что поддержка кросс-платформенного фремворка закончится — намного выше, чем вероятность того же события в отношении мобильной ОС. На самом деле вопрос довольно сложный. ОС могут закрываться так же, как фреймворки (пример Windows Phone совсем свеж). Кроме того, внутри нативной разработки могут отдельные технологии также могут закрываться и иногда код на кросс-платформенных фреймворках обладает большей живучестью. Пример этого есть в области игр и мультимедиа — Apple собирается отправить на покой технологию OpenGL в своих ОС и всем, кто писал нативные 3D-приложения придётся их полностью переписывать для выпуска новых версий. В то же время для тех, кто пользовался кросс-платформенными игровыми движками (например Unity), обновление не потребует никаких дополнительных усилий. Технически, приложения гибридного типа представляют собой HTML-страницу, отображаемую во встроенном браузере. В целом для этого подхода фреймворк не обязателен, но Cordova обеспечивает набор плагинов для доступа к возможностям платформы, из-за чего как правило используют её. Гибридный подход позволяет переиспользовать не только навыки разработчиков, но и код, написанный для веб-сайтов. Количество библиотек для HTML/JS заметно превосходит количество таковых для нативных приложений. Из интересного, сюда относятся, например, Google Analytics, или богатый выбор рекламных сетей. Сам по себе современный JavaScript использует JIT-компиляцию, хорошо оптимизирован и работает быстро, но построение интерфейса на основе DOM-дерева — не очень эффективный процесс. Использование современных JS-фреймворков даёт дополнительный уровень нагрузки. Для слабых телефонов и/или при активном использовании интерактивных элементов, это может быть проблемой. Это довольно неформальный, но очень важный пункт. Сайт в браузере реагирует на жесты и отображается немного не так, как мобильное приложение. Наиболее заметный элемент этого ощущения, задержку 300ms при нажатиях, Cordova решает, но многие другие детали остаются. На старых версиях Android (до версии 5), WebView был частью платформы и не обновлялся автоматически. Соответственно, использовать современные возможности браузеров в гибридных приложениях на этих устройствах не получится. Как следствие, гибридные приложения либо ограничивают минимальную версию Android (оставляя за бортом около 13% устройств на данный момент), либо включают WebView в код приложения (проект CrossWalk), увеличивая размер приложения на несколько десятков мегабайт. Быстрое создание одноразовых приложений. При наличии существенного бюджета на разработку, гибридным подходом, как правило, брезгуют. Основой всех основных гибридных фреймворков является Cordova, предоставляющая доступ к нативным плагинам. PhoneGap предоставляет инструментарий для сборки поверх Cordova, в то время как Ionic представляет собой фреймворк и набор компонентов для построения в ней пользовательских интерфейсов. Важно отметить, что при таком подходе, пользовательский интерфейс и бизнес-логика выполняются в разных процессах, взаимодействующих через мост (“bridge”). С этим связан ряд минусов подхода. У этого подхода есть несколько вариантов реализации. Xamarin использует язык C#, компилируя его в нативный код платформы. В целом, такой подход обеспечивает достаточно небольшой размер приложения и достаточно высокую скорость работы. Большинство фреймворков этого подхода используют JavaScript для обработки бизнес-логики. Xamarin не только использует нативные компоненты интерфейса, но и описывает их в формате, принятом для каждой платформы. Xamarin Forms и Appcelerator используют собственный набор виджетов, который преобразуется в подходящие компоненты интерфейса каждой платформы. React Native использует обёртки вокруг нативных компонентов интерфейса. Соответственно, интерфейс описывается для каждой платформы отдельно, но способ описания един. Во-первых, внешний вид и “ощущение” от приложения полностью совпадают с нативными приложениями. Во-вторых, это позволяет использовать в приложениях библиотеки нативных интерфейсов. Использовать нативную рекламу (Native Ads), ориентированную на мобильные приложения, в других подходах не получится. Правда и для этого подхода набор соответствующих библиотек очень ограничен. Я знаю только о поддержке Facebook Native Ads в React Native. Многие фреймворки этой группы спроектированы так, чтобы разработчики из других областей могли с минимальными затратами обучиться созданию мобильных приложений. Для React Native — это React, для Xamarin — .NET и т.д. Формат этого минуса зависит от классификации фреймворка по способу описания интерфейса: Xamarin позволяет использовать почти все возможности платформ, но приходится тратить много времени на интерфейсы для каждой платформы. В итоге трудозатраты становятся ненамного меньше, чем при нативной разработке. Xamarin Forms и Appcelerator позволяют описывать интерфейсы только один раз, но работают с сильно ограниченным подмножеством нативной функциональности (не больше, чем минимальное пересечение множеств возможностей каждой платформы, если быть формальным). React Native находится посредине, совмещая оба недостатка, но в менее выраженной форме.
Производительность взаимодействия с интерфейсом
Здесь вступает в игру фактор исполнения интерфейсов и бизнес-логики в разных процессах. Когда нужно обмениваться через мост большими объёмами информации с высокой скоростью (сложная анимация с высокой частотой), в данном подходе могут возникать сложности. Утечки памяти могут возникать в любом приложении, но с большинством стандартных ситуаций отлично справляются сборщики мусора. Проблема кросс-платформенных приложений с нативным интерфейсом опять в том, что они выполняются в двух процессах, имеющих раздельные сборщики мусора. Если объект бизнес-логики ссылается на объект интерфейса, этот объект интерфейса не является мусором, т.к. на него есть ссылка из моста. Если объект интерфейса ссылается обратно на объект бизнес-логики, они не будут считаться мусором даже если больше никаких ссылок на них не будет. Шансы встретить проблему и её масштаб напрямую зависят от приложения. Если в нём активно создаются и удаляются объекты, связанные с интерфейсом (как в бесконечном скроллинге) вероятность утечки вырастает. Если эти объекты большие (например изображения), вырастает влияние утечки. Собственно, эта проблема присутствует и при работе с нативными плагинами, которые тоже выполняются в отдельном процессе. Но там, в большинстве случаев, либо нет такого интенсивного манипулирования большими объектами, либо взаимодействие идёт в строго процедурном подходе, без перекрёстных ссылок. Приложения с полностью нативным интерфейсом, особенно при наличии специалистов в смежных технологиях. Имеет поддержку Facebook и использует подход самого популярного JS-фреймворка React, за счёт чего очень популярен. Недавняя статья об отказе AirBnb от React Native наделала много шума, но если осознавать риски, может быть очень эффективным решением. Помимо основного подхода имеет библиотеку Xamarin.Forms, которая позволяет эффективно и кроссплатформенно проектировать простые интерфейсы. Активно поддерживается Microsoft. При работе с ASP.NET на сервере позволяет дополнительно съэкономить определённый объём работы за счёт использования общих классов бизнес-логики на сервере и в мобильном приложении. Сделан по образцу React Native для разработчиков, владеющих другими JS-фреймворками (Angular и Vue.js). Менее популярен, но имеет ряд более современных решений в архитектуре. Этим подходом пользуются почти все игровые движки, но они находятся за рамками данной статьи. Принцип данного подхода — приложение использует собственный код и собственную отрисовку пользовательского интерфейса. По сути, приложение, самостоятельно рисующее свой интерфейс, выполняет те же операции, что ОС в нативном интерфейсе. В теории оно может быть даже быстрее, т.к. нет переключения между процессом и ядром, но на практике другие факторы влияют на скорость отрисовки конкретного интерфейса, играют намного большую роль. Нативные приложения пользуются готовыми компонентами интерфейса и имеют некоторые ограничения в том, что с ними можно сделать. В свою очередь, приложения, сами рисующие свой интерфейс, не имеют таких ограничений и могут свободно смешивать готовые элементы с индивидуальной отрисовкой. Данные минусы актуальны только для приложений, имитирующих стандартный интерфейс ОС. Как уже говорилось, для дизайнерских интерфейсов и игр данный подход является оптимальным. Приложения с таким подходом вынуждены нести с собой код для отрисовки всех элементов интерфейса, включая условно стандартные. Это сказывается как на размере приложения при установке, так и на оперативной памяти при работе. Если первую проблему можно минимизировать эффективным Tree Shaking (как это делают последние версии Flutter), то по оперативной памяти эти приложения стабильно проигрывают нативным. Впрочем, эта проблема характерна и для других кросс-платформенных фреймворков. По умолчанию, приложение выглядит одинаково на всех платформах, что может создавать дискомфорт для пользователей. Для решения этих проблем используются темы, но создать ощущение полностью нативного приложения они не могут. Но есть и более крупный минус — с таким подходом сложнее всего использовать сторонние элементы интерфейса, созданные для нативных приложений (включая упомянутые ранее Native Ads). Приложения общего пользования, особенно с дизайнерским интерфейсом. Flutter продвигается компанией Google как основной фреймворк кросс-платформенной разработки и основа интерфейса их будущей ОС Fuscia. Пока фреймворк очень молод (в стадии Release Preview) и не очень распространён, но быстро набирает популярность. Использует язык Dart (с компиляцией в нативный код). Имеет все плюсы и минусы молодости — продуманную архитектуру с учётом ошибок предшественников, но достаточно ограниченную экосистему. Пользуется популярностью у разработчиков десктопного QT. При разработке может использоваться JavaScript. Без поддержки крупных компаний не пользуется особой популярностью. Ещё один не очень популярный фреймворк, который интересен, в первую очередь тем, что это единственный фреймворк в списке, который использует язык Python. Для разработчиков, знакомых только с этим языком (а таких много в некоторых областях информационных технологий), это может иметь решающее значение. По работе памяти в Xamarin и похожих фреймворков
Сравнение производительности нативных приложений, Flutter и React Native Product Manager, Analyst