![](https://habrastorage.org/webt/cg/9h/ou/cg9hourtlmzdlzok6fwfbp0uzko.jpeg)
Передаю слово автору.
1. Настройка окружения
Первое, с чем сталкиваются разработчики Xamarin.Forms — длительная сборка проектов, гораздо дольше комфортного уровня. Это связано с тем, что при сборке создается и обновляется большое количество файлов.
![](https://habrastorage.org/webt/_d/x-/4l/_dx-4lyweyaj4isbffaiklg7smi.png)
Чтобы ускорить сборку, достаточно перенести папку с исходными кодам на RAM-диск. Это когда часть оперативной памяти используется в качестве локального диска или папки в файловой системе Mac’а. Чтобы ничего не терялось, данные с RAM-диска сохраняются на SSD по таймеру. Если у вас все еще стоит обычный жесткий диск (HDD), то стоит обязательно обновиться на SSD. RAM-диск 4 ГБ + хороший SSD заметно ускоряют сборку. Примерно на порядок.
В сети вы можете найти различные утилиты для работы с RAM-дисками, а также инструкции по работе с ними.
2. Узкие места Xamarin
Как и при работе с любым другим инструментом, стоит понимать особенности Xamarin.Forms при создании приложений. Если задача приложения удивить пользователя большим количеством анимаций и нестандартным интерфейсом, то кроссплатформенные фреймворки — не лучший выбор. А вот для относительно статических бизнес-приложений они подойдут гораздо лучше.
Так как Xamarin.Forms это надстройка над классическими Xamarin.iOS и Xamarin.Android, то стоит понимать их узкие места. Ниже представлена архитектура приложения на Xamarin.iOS и Xamarin.Android.
![](https://habrastorage.org/webt/xh/vq/c2/xhvqc22jdav2kramcjgxaqwle5k.png)
Xamarin-приложение состоит из двух частей: нативной и кроссплатформенной (среда Mono). Эти части общаются друг с другом через специальный мост (bridge). Вокруг каждого нативного класса и типа создается обертка для окружения Mono. При передаче данных и вызове методов в переходе “нативный”-”кроссплатформенный” происходит небольшое падение производительности, поэтому стоит обязательно это учитывать.
Например, при вычислениях и работе с данными использовать типы и классы из окружение Mono (C#/.NET), а не нативные (nfloat). Также следуем минимизировать управление UI из C#-кода. Например, не осуществлять анимации или интенсивно обновлять UI в цикле из кроссплатформенной части. Не то, чтобы были какие-то тормоза, но при больших вычислениях и массивном управлении UI (большое количество элементов с асинхронно приходящими данными и различные анимации) могут быть заметные пользователю и ненужные задержки.
В полной мере это относится и к Xamarin.Forms, который решает непростую задачу предоставить одновременно гибкий и высокопроизводительный слой работы с пользовательским интерфейсом и платформенной функциональностью для iOS, Android (а также Windows UWP, mac OS, GTK# и Tizen до кучи, но их в расчет не берем). И несмотря на то, что большинство “детских” болезней уже преодолено, в нем все еще остаются узкие места, которые следует обязательно учитывать при разработке.
3. XAML
В первых версиях Xamarin.Forms были явные проблемы с производительностью XAML-страниц. Можно было описать тот же пользовательский интерфейс на C#-коде — это уменьшало время создания (вызов конструктора!) Page или View более чем на 300 мс на устройствах средней производительности. Для сложных компонентов это время было еще больше. Свою лепту в увеличение времени холодного запуска приложения вносил App.xaml. Если в нем было много стилей и других ресурсов, то запуск приложения мог увеличиться на заметные секунды. Опять же на пустом месте. Особенно эта проблема была актуальна для Android, где простой уход от использования App.xaml и стартовых Page (например, MainPage.xaml и MenuPage.xaml) в пользу C#-кода сокращал время запуска приложения иногда в два раза.
Добавление компиляции XAML заметно уменьшило это время. В Xamarin.Forms 2.5.0 получились такие показатели для страницы Hello World:
- XAML без компиляции 441 мс
- XAML с компиляцией 188 мс
- Код на C# 182 мс.
![](https://habrastorage.org/webt/na/8b/kf/na8bkfqfhe_k-azblumiiboewha.png)
Как видим, в каких-то редких случаях, например для ячеек ListView, где важна максимальная производительность, можно использовать описание интерфейса на C#. А для всех остальных сценариев можно заметно ускорить работу UI за счет добавления строки [assembly: XamlCompilation(XamlCompilationOptions.Compile)]
в код вашего проекта.
![](https://habrastorage.org/webt/ra/bd/qk/rabdqkwqfvfr4tvegcgfhgkczfe.png)
Также стоит учитывать, что для вложенных XAML (XAML-страница использует внешние View, также описанные за XAML) со сложной версткой и большим количеством элементов время инициализации будет выше приведенного примера Hello World. Компиляция XAML может заметно ускорить создание Page и View, а мы переходим к следующему пункту.
4. Layout
Чтобы обеспечить кроссплатформенную верстку пользовательского интерфейса для iOS, Android (и других платформ) в Xamarin.Forms реализованы свои механизмы компоновки (Layout) интерфейсных элементов. В нативном iOS и Android есть свои механизмы Layout, но они не подходят напрямую для кроссплатформенных приложений, так как имеют специфичную для каждой платформы логику.
![](https://habrastorage.org/webt/gp/hc/3_/gphc3_asmh0k10jwimvdvks52uy.png)
Чтобы правильно скомпоновать страницы, фреймворк должен знать размеры вложенных на страницу элементов, а это запускает обход всего дерева дочерних контролов. В будущих релизах Xamarin.Forms обещают множество оптимизаций в том числе механизмах Layout. Но в настоящее время стоит помнить о простом правиле — чем больше точных цифр будет указано у UI-элементов и чем меньше будет уровень вложенности, тем быстрее будет отрабатывать компоновка. Например, можно указать AbsoluteLayout.LayoutBounds="0.1,0.1,0.3,0.3" или WidthRequest=”300”
в свойствах UI-элемента.
Также иногда возникает потребность в реализации своих Layout. В них вы можете для каждого UI-элемента вычислить точные размеры, например, относительно размеров родительского View. Это уберет долгий пересчет координат и поможет, опять же, ускорить тяжелые ячейки в ListView.
Подробнее про оптимизацию Layout
Подробнее про создание своих Layout
5. Обновление UI и правильные ViewModel
Еще одной болью всех UI-подсистем, включая Xamarin.Forms, является пересчет размеров при обновлении данных. Это пресловутая проблема обновления дерева визуальных объектов, для решения которой в Web-разработке и появился ReactJS с аналогами. В mobile эта проблема есть, хотя и не такая острая.
В Xamarin.Forms используется архитектура MVVM, в которой при обновлении свойства ViewModel вызывается событие PropertyChanged, новые данные приходят в UI и View может запросить пересчет размеров и координат у всей страницы целиком.
![](https://habrastorage.org/webt/rk/co/as/rkcoasbf1zvgl9r1_cfuve8gura.png)
Если вы обновляете 10 разных полей, то произойдет 10 циклов компоновки по всему дереву элементов на странице, а это заметные глазу пользователя 10 циклов. Для того, чтобы вместо 10 циклов запускать 1, следует использовать 1 модель с нужными 10 полями. Теперь модель и View будут обновляться целиком — одно событие PropertyChanged вместо 10.
![](https://habrastorage.org/webt/sp/0x/md/sp0xmdwpmpj92ujjxnbozedfcsc.png)
Итак, в нашей сегодняшней статье мы рассмотрели основные узкие места Xamarin.Forms и подходы, которые нивелируют их эффект. И это еще не все на сегодня!
Приглашение на Xamarin Day
Если у вас есть вопросы по Xamarin, и вам интересна разработка кроссплатформенных приложений, то напоминаю о предстоящем Xamarin Day, который пройдет 31 января в Москве. На нем вы сможете не только узнать о best practices, но и получить живую консультацию по вашему коду! Показываете куски вашего кода, по которым у вас есть вопросы (проблемы, баги, архитектура, низкая производительность и т.д.) и получаете бесплатную консультацию от экспертов прямо на месте. Чтобы подать заявку на индивидуальную консультацию по вашему проекту — напишите краткую информацию о нем и проблеме на events-techdays@microsoft.com.
Подробнее о мероприятии здесь.
Вступайте в наш уютный чат Xamarin Developers в Telegram.
Оставайтесь на связи и обязательно приходите на Xamarin Day!
Об авторе
Другие статьи автора вы можете найти в нашей колонке #xamarincolumn.
Комментарии (5)
andreyverbin
30.01.2018 11:29В Xamarin.Forms 2.5.0 получились такие показатели для страницы Hello World:
XAML без компиляции 441 мс
XAML с компиляцией 188 мс
Код на C# 182 мс.182 мс это 5 кадров в секунду и это hello world. Если так для каждой незакешированной ячейки, то Xamarin.Forms есть куда еще расти (мягко говоря). Мне кажется вы могли замерить время jit (на android), либо время подгрузки каких-либо библиотек.
Я как-то мерял скорость маршалинга из C# в ObjC и назад и получалось, что разницы практически нет. Затраты на p/Invoke теряются на фоне типичных операций типа выставления текста (label.Text) и составляют доли %. То есть сам Xamarin оказывает незначительное влияние и точно не медленее ObjC/Swift. Forms другое дело, про них я ничего не знаю, всегда работал только с Native.
devious
30.01.2018 17:50182 мс это не «5 кадров в секунду» (откуда вообще fps взялись?), а просто 182 мс (~1/5 секунды) задержки перед открытием экрана или первого в приложении создания контрола — дальше все кешируется. Перфекционизм это хорошо, конечно :)
В плане роста и развития — да, в XF еще много всякого будет дорабатываться и улучшаться.
ruma46
30.01.2018 19:50Прочитав Ваш совет, установил утилиту для RAM disk'а, ту, что выдается первым номером по ссылке в статье. Произвёл замеры (правда, не для Xamarin, а для C++/MFC проекта). Получилась не такая уж и большая выгода в моём случае. На полную перекомпиляцию ушло 4:40 на RAM диске против 4:55 на SSD. 15 секунд — это 5% разницы всего, а заморочки с диском и возможная потеря данных в случае непредсказуемого выключения, на мой взгляд, не стоят того.
devious
31.01.2018 09:03> правда, не для Xamarin, а для C++/MFC проекта
Статья не про MFC, а про Xamarin.Forms :)
Juels
Очень здорово! За Layout отдельное спасибо, всегда не с первого раза попадал правильно.
Жаль, что все самые интересные доклады проходят только в Москве :(