При создании мобильных приложений возникает множество различных хотелок в духе «как было бы хорошо, если бы…» И можно подставлять пункты, которые требуют улучшения. У начинающих разработчиков в этот список с высокой вероятностью войдет и пункт «если бы работало быстрее …» В данной статье будут собраны рекомендации, которые помогут начинающим разработчикам Xamarin.Forms обойти узкие места фреймворка и инструментов сборки. А начнем мы с железа.
Передаю слово автору.
Чтобы ускорить сборку, достаточно перенести папку с исходными кодам на RAM-диск. Это когда часть оперативной памяти используется в качестве локального диска или папки в файловой системе Mac’а. Чтобы ничего не терялось, данные с RAM-диска сохраняются на SSD по таймеру. Если у вас все еще стоит обычный жесткий диск (HDD), то стоит обязательно обновиться на SSD. RAM-диск 4 ГБ + хороший SSD заметно ускоряют сборку. Примерно на порядок.
Как и при работе с любым другим инструментом, стоит понимать особенности Xamarin.Forms при создании приложений. Если задача приложения удивить пользователя большим количеством анимаций и нестандартным интерфейсом, то кроссплатформенные фреймворки — не лучший выбор. А вот для относительно статических бизнес-приложений они подойдут гораздо лучше.
Xamarin-приложение состоит из двух частей: нативной и кроссплатформенной (среда Mono). Эти части общаются друг с другом через специальный мост (bridge). Вокруг каждого нативного класса и типа создается обертка для окружения Mono. При передаче данных и вызове методов в переходе “нативный”-”кроссплатформенный” происходит небольшое падение производительности, поэтому стоит обязательно это учитывать.
Например, при вычислениях и работе с данными использовать типы и классы из окружение Mono (C#/.NET), а не нативные (nfloat). Также следуем минимизировать управление UI из C#-кода. Например, не осуществлять анимации или интенсивно обновлять UI в цикле из кроссплатформенной части. Не то, чтобы были какие-то тормоза, но при больших вычислениях и массивном управлении UI (большое количество элементов с асинхронно приходящими данными и различные анимации) могут быть заметные пользователю и ненужные задержки.
В полной мере это относится и к Xamarin.Forms, который решает непростую задачу предоставить одновременно гибкий и высокопроизводительный слой работы с пользовательским интерфейсом и платформенной функциональностью для iOS, Android (а также Windows UWP, mac OS, GTK# и Tizen до кучи, но их в расчет не берем). И несмотря на то, что большинство “детских” болезней уже преодолено, в нем все еще остаются узкие места, которые следует обязательно учитывать при разработке.
В первых версиях 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:
Как видим, в каких-то редких случаях, например для ячеек ListView, где важна максимальная производительность, можно использовать описание интерфейса на C#. А для всех остальных сценариев можно заметно ускорить работу UI за счет добавления строки [assembly: XamlCompilation(XamlCompilationOptions.Compile)]
в код вашего проекта.
Также стоит учитывать, что для вложенных XAML (XAML-страница использует внешние View, также описанные за XAML) со сложной версткой и большим количеством элементов время инициализации будет выше приведенного примера Hello World. Компиляция XAML может заметно ускорить создание Page и View, а мы переходим к следующему пункту.
Чтобы обеспечить кроссплатформенную верстку пользовательского интерфейса для iOS, Android (и других платформ) в Xamarin.Forms реализованы свои механизмы компоновки (Layout) интерфейсных элементов. В нативном iOS и Android есть свои механизмы Layout, но они не подходят напрямую для кроссплатформенных приложений, так как имеют специфичную для каждой платформы логику.
Чтобы правильно скомпоновать страницы, фреймворк должен знать размеры вложенных на страницу элементов, а это запускает обход всего дерева дочерних контролов. В будущих релизах Xamarin.Forms обещают множество оптимизаций в том числе механизмах Layout. Но в настоящее время стоит помнить о простом правиле — чем больше точных цифр будет указано у UI-элементов и чем меньше будет уровень вложенности, тем быстрее будет отрабатывать компоновка. Например, можно указать AbsoluteLayout.LayoutBounds="0.1,0.1,0.3,0.3" или WidthRequest=”300”
в свойствах UI-элемента.
Также иногда возникает потребность в реализации своих Layout. В них вы можете для каждого UI-элемента вычислить точные размеры, например, относительно размеров родительского View. Это уберет долгий пересчет координат и поможет, опять же, ускорить тяжелые ячейки в ListView.
Подробнее про оптимизацию Layout
Подробнее про создание своих Layout
Еще одной болью всех UI-подсистем, включая Xamarin.Forms, является пересчет размеров при обновлении данных. Это пресловутая проблема обновления дерева визуальных объектов, для решения которой в Web-разработке и появился ReactJS с аналогами. В mobile эта проблема есть, хотя и не такая острая.
В Xamarin.Forms используется архитектура MVVM, в которой при обновлении свойства ViewModel вызывается событие PropertyChanged, новые данные приходят в UI и View может запросить пересчет размеров и координат у всей страницы целиком.
Если вы обновляете 10 разных полей, то произойдет 10 циклов компоновки по всему дереву элементов на странице, а это заметные глазу пользователя 10 циклов. Для того, чтобы вместо 10 циклов запускать 1, следует использовать 1 модель с нужными 10 полями. Теперь модель и View будут обновляться целиком — одно событие PropertyChanged вместо 10.
Итак, в нашей сегодняшней статье мы рассмотрели основные узкие места Xamarin.Forms и подходы, которые нивелируют их эффект. И это еще не все на сегодня!
Если у вас есть вопросы по Xamarin, и вам интересна разработка кроссплатформенных приложений, то напоминаю о предстоящем Xamarin Day, который пройдет 31 января в Москве. На нем вы сможете не только узнать о best practices, но и получить живую консультацию по вашему коду! Показываете куски вашего кода, по которым у вас есть вопросы (проблемы, баги, архитектура, низкая производительность и т.д.) и получаете бесплатную консультацию от экспертов прямо на месте. Чтобы подать заявку на индивидуальную консультацию по вашему проекту — напишите краткую информацию о нем и проблеме на events-techdays@microsoft.com.
Вступайте в наш уютный чат Xamarin Developers в Telegram.
Оставайтесь на связи и обязательно приходите на Xamarin Day!
К сожалению, не доступен сервер mySQL