Xamarin: Готовим Xamarin.Forms, настройка окружения и первые шаги

Slava Chernikoff
Feb 24, 2017 · 5 min read

Вторая статья в цикле “Рецепты для Xamarin-разработчиков”.

Оригинал: https://habrahabr.ru/company/microsoft/blog/303630/


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

Правильные пчелы

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

Начнем мы с компьютеров, на которых будет идти разработка.

В своей практике в качестве основной среды мы используем Windows 10 и Visual Studio 2015, поэтому столкнулись с тем, что сборка приложений (в первую очередь Android) занимает непозволительно долго времени.

Мы стали выяснять в чем здесь дело и выявили несколько узких мест, замедляющих разработку:

  • во время сборки Android-приложений Xamarin может в первый раз выкачивать необходимые исходные коды дополнительных компонентов и складывать их на локальный диск для последующего использования. Здесь ничего поделать нельзя, поэтому просто будьте готовы к тому, что первая сборка может занять у вас минут 10.
  • дальше начинается самое интересное: во время сборки Xamarin гененерирует и использует большое количество файлов (до 200 МБ в папках bin и obj, плюс еще целая куча в папке Temp), поэтому сразу забудьте про использование жестких дисков (HDD) на рабочих станциях — чем быстрее будет у вас SSD, тем лучше.

Особо продвинутым можно рекомендовать использование ОЗУ в качестве раздела для хранения проектов и системной Temp-папки — для этого нужно использовать одну из программ класса RAM Disk.Точных измерений при использовании SSD+RAM Disk не производили, но на глаз разница колоссальна.

Итак, считаем, что с правильным окружением мы разобрались.

XAML — зло или благо

Одним из важных механизмов Xamarin.Forms является возможность использования XAML (на базе XML) для описания интерфейса пользователей.

На наш взгляд, это является хорошим решением с точки зрения технологии производства, так как происходит отделение логики от описания интерфейса и сама по себе UI-разработка становится очень похожей на HTML-верстку, даже стили контролов можно задавать в одном месте как с CSS (ликбез по стилям по ссылке), включая небольшой тюнинг для разных платформ (Device.OnPlatform).

Все было бы отлично, если бы не одно большое “НО”, которое характерно для Xamarin.Forms версии 1.х — XAML-файлы интерпретировались на лету, включая создание всех необходимых контролов и их размещение на экране. Из-за этого каждое открытие нового окна со сложной компоновкой происходило дольше, чем хотелось бы.

В версии 2.0 этот недостаток устранили, реализовав предварительную компиляцию XAML. Использовать ее можно как для отдельных страниц и View на XAML, так и для всего проекта целиком.

Включаем компиляцию отдельной XAML-страницы (файл MainPage.xaml.cs, например)

using Xamarin.Forms.Xaml;
...
[XamlCompilation (XamlCompilationOptions.Compile)]
public class MainPage : ContentPage
{
...
}

Активируем компиляцию по всему проекту — добавляем новую строку в конец файла Properties/AssemblyInfo.cs в PCL-проекте:

...
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

Однако в некоторых случаях, особенно при реализации ячеек для ListView, может быть эффективнее описать компоновку для ViewCell/View/Page руками в коде на C# или спуститься на уровень iOS/Android.

Но к ListView мы еще вернемся.

Картинки, иконки и производительность

Первое, с чем сталкиваются начинающие разработчики на Xamarin.Forms — залипания при прокрутке в списках на базе ListView. Чего греха таить, это одно из болезненных мест платформы, отбрасывающее тень на весь остальной функционал, так как списки используются в больших количествах и практически во всех приложениях.

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

Стандартный компонент для отображения Image хорош, даже поддерживает кеширование, но он мало пригоден при использовании в реальных проектах. Когда год назад мы уперлись в ограничения Image, пришлось сделать свой простенький кешер изображений с автоматическим масштабированием — для отображения в ячейку передавалась уменьшенная версия, так как с большими изображениями списки просто умирали (это не проблема Xamarin.Forms, а вообще любой разработки приложений, но на Xamarin.Forms она была очень острой).

Потом мы перепробовали несколько различных библиотек и в конце концов остановились на превосходном компоненте под названием FFImageLoading.

Он доступен в Nuget () и позволяет решить сразу несколько задач:

  • фоновая загрузку изображений с возможностью повтора запросов;
  • использование placeholder во время загрузки;
  • автоматическое масштабирование изображений до размеров контрола, включая удаление Alpha-канала на Android для еще большей производительности;
  • возможность применения трансформаций к изображениям после загрузки (обрезать до круга или другой формы, наложить blur или другой спец. эффект);
  • fade-анимация появления изображения после загрузки.

Вот так использовать компонент при разработке на XAML:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Sample.Pages.MainPage"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
Title="FFImageLoading Demo">
<ContentPage.Content>
<ffimageloading:CachedImage HorizontalOptions="Center" VerticalOptions="Center"
WidthRequest="300" HeightRequest="300"
DownsampleToViewSize="true"
Source = "https://unsplash.it/600/600/?random"
LoadingPlaceholder = "placeholder.png">
</ffimageloading:CachedImage>
</ContentPage.Content>
</ContentPage>

Вот так можно еще немного улучшить работу FFImageLoading (класс AppDelegate.cs для iOS и MainActivity.cs для Android):

var config = new Configuration
{
HttpClient = new HttpClient(new ModernHttpClient.NativeMessageHandler()), //используем быстрые библиотеки для загрузки
FadeAnimationDuration = 250, //ускоряем анимацию появления
};
FFImageLoading.ImageService.Instance.Initialize(config);

Работаем со спискам в Xamarin.Forms

С помощью FFImageLoading мы оптимизировали отображение изображений в списках и теперь можем перейти к следующему шагу.

Первая рекомендация касается механизмов работы ListView. В ранних версиях Xamarin.Forms не поддерживался механизм повторного использования созданных ячеек, что приводило к созданию экземпляров ViewCell каждый раз при необходимости их отображения — это и было первой серьезной причиной залипаний при скроле.

В последних версиях Xamarin.Forms реализован механизм повторного использования созданных ячеек и для его активации необходимо задать свойству CachingStrategy значение ListViewCachingStrategy.RecycleElement.

Реализация в XAML:

<ListView CachingStrategy="RecycleElement">
...
</ListView>

Реализация на C#:

var listView = new ListView(ListViewCachingStrategy.RecycleElement);

После этого созданные экземпляры ViewCell начинают использоваться повторно и к ним просто подключаются (через Binding) необходимые модели данных по мере отображения.

Еще один интересный способ подсказал мой коллега Кирилл — просто останавливать загрузку и отображение картинок во время прокрутки списка. Этот способ также рекомендуют использовать и создатели FFImageLoading.

Для этого необходимо сделать свой рендерер для ListView для каждой платформы и переопределить события прокрутки.

Пример для Android:

_listView.ScrollStateChanged += (object sender, ScrollStateChangedEventArgs scrollArgs) => {
switch (scrollArgs.ScrollState)
{
case ScrollState.Fling:
ImageService.Instance.SetPauseWork(true); // ставим загрузку картинок на паузу
break;
case ScrollState.Idle:
ImageService.Instance.SetPauseWork(false); // возобновляем загрузку картинок
// здесь должен быть ваш код отображения изображений в отображаемых ячейках
ShowMyImage();
break;</p>
}
};

При необходимости вы также можете создать свои реализации ViewCell через механизм Custom Renderer для каждой платформы — такой подход может быть актуален для сложных ячеек с большим количеством встроенных контролов.

Также хорошие результаты при работе с большими объемами данных и сложными ячейками позволяет достичь использование нативных компонентов UICollectionView (для iOS) и RecyclerView (для Android), но это решение можно рекомендовать продвинутым разработчикам.

Пример использования данных классов представлен в библиотеке TwinTechs — обязательно к знакомству, так как там есть видео-ролики с результатами оптимизации.

Итак, на первых порах освоения Xamarin.Forms необходимо уделить особое внимание работе со списками и подходам к повышению производительности. Это позволит не отвлекаться на данные особенности в последующей разработке.

Заключение

В нашей сегодняшней статье мы рассмотрели базовые механизмы оптимизации рабочего окружения и первые шаги к разработке бизнес-приложений на базе Xamarin.Forms.

В следующем материале мы расскажем об использовании иконочных шрифтов в стиле Font Awesome, инструмента Fody для сокращения кода у ViewModel и механизмах ручной отрисовки своих интерфейсных элементов с помощью библиотеки NControlView.

Binwell

Разработка облачных и мобильных решений под заказ

Slava Chernikoff

Written by

Tech Lead at Binwell / Microsoft MVP

Binwell

Binwell

Разработка облачных и мобильных решений под заказ

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade