Pull to refresh
62.43
ГК ICL
Цифровые технологии для бизнеса

Работаем с Xamarin: опыт разработки на двух проектах

Reading time 10 min
Views 36K
Хочу поделиться опытом разработки с использованием Xamarin (звучит как Замарин) на двух крупных проектах. Первый проект был под Windows Store и iOS, второй только под Андроид, но c использованием Xamarin.Forms. Xamarin быстро развивается, поэтому некоторые описываемые здесь моменты, могли уже стать неактуальными. Например: ещё летом мы переживали из-за дикого потребления памяти в Андроид и даже вручную вызывали сборщик мусора в некоторых местах, но в конце лета вышло обновление, которое закрыло многие наши проблемы с памятью.

image

Когда использовать Xamarin и Xamarin.Forms


Не хочу показаться Капитаном Очевидность для некоторых, но повторю, то, что уже неоднократно обсуждалось на Хабре и различных форумах: нет никакого смысла использовать Xamarin в «обычных» мобильных приложениях. Под «обычными» мобильными приложениями я понимаю приложения, обладающие следующими характеристиками:
  • содержат немного бизнес-логики;
  • вся бизнес-логика реализована на сервере;
  • главным в приложении является удобный пользовательский интерфейс.

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

Основной мотивацией использовать Xamarin должно быть наличие большого объёма кода, который работает на разных платформах и проще, когда он написан на одном языке, используя кроссплатформенный фреймворк. Мы писали приложение в области розничной торговли под Windows и iOS. В этом проекте только расчет цены товара занимал несколько тысяч строк кода.

Xamarin.Forms – часть Xamarin, которая позволяет кроссплатформенно создавать пользовательский интерфейс с помощью XAML. Можно использовать Xamarin без Xamarin.Forms, создавая интерфейс с помощью классов-обёрток над нативными классами. Xamarin без Forms нельзя использовать для кросс-платформенного создания пользовательского интерфейса.

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

image

Какой тип проекта выбрать


В Xamarin есть два способа создать проект в солюшене:

  1. Shared Project
  2. PCL Library.

Первый – реализация Xamarin, аналог shared project в Windows Store-приложениях, когда проект не компилируется в отдельную сборку, а как бы встраивается в основной проект, в котором запускается приложение.

image

Главным уроком для нас стало то, что нет никаких причин использовать замариновский тип проекта Shared Project (.shproj), а нужно использовать .NET-овский PCL (Portable Class Library). Главный недостаток Shared Project в том, что в нём можно написать код, который не будет работать на других платформах, и он скомпилируется, а когда начнёшь разрабатывать под другую платформу, с удивлением обнаружишь, что много кода в этой платформе не поддерживается. PCL же такого недостатка лишен. При создании этого типа проекта нужно указать, какие платформы должны поддерживаться. Если захочешь использовать, например, что-то из Андроида, то он просто не скомпилируется и не даст добавить не поддерживаемые ссылки (references) в проект. Бонусом вы получаете дополнительную уверенность, что в команде никто не сможет поддастся соблазну написать что-то в общем проекте по-быстрому, но нативно, нежели правильно, но с большими усилиями, например, вынеся нативный код в нативный проект, и подключив его в PCL через Dependency Injection.

Будьте готовы к утечкам памяти


Неприятным, но отнюдь, не неожиданным моментом были утечки памяти в iOS. Мир iOS и мир Xamarin – разные миры. В одном, память очищается посредством подсчёта ссылок (automatic reference counting), а в другом это делает сборщик мусора (garbage collector). Поэтому, когда мы что-то вызываем в мире Xamarin, имеющее связь с миром iOS – а это работа практически с любым нативным контролом – создаётся ссылка из того мира, в уютный managed мир, которую GC не может очистить, ведь он не знает активна ещё эта ссылка или нет. Бороться с этим приходится очень некрасивым способом. При закрытии страницы приходится присваивать null всем внешним объектам, и отписываться от обработчиков событий. Занятие довольно муторное.

В iOS для профилирования памяти можно использовать Apple Instruments. Для Андроида такой возможности нет, стандартные утилиты могут работать только с нативными приложениями. Также Xamarin выпустили бета-версию инструмента Xamarin Profiler. Мы попробуем её использовать для Андроида в ближайшие недели.

В Андроид всё намного лучше, но всё равно нужно иметь в виду, что одновременно работают два сборщика мусора. Один в мире Android Runtime (до версии 5.0 Dalvik Runtime), другой в мире Mono Runtime. Может возникнуть аналогичная ситуация, когда ни тот, ни другой не смогут удалить неиспользуемые ресурсы. Тем не менее, после установки последних обновлений, при работе с пользовательским интерфейсом, контролами, значительных утечек мы не заметили. По мелочи же, всегда что-то происходит, пытаться выяснить глубинные причины, в условиях, когда следующее обновление может всё поменять, не разумно. Поэтому во всех наших страницах, при переходе на следующую страницу мы вызываем такой некрасивый код:

this.Content = null;

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

Опыт MVVM без Forms


В проекте под Windows Store и iOS у нас общая логика, но пользовательские интерфейсы написаны нативно. Т.к. для Windows мы писали на XAML, то решили, чтоб будем использовать MVVM, а iOS будет вызывать существующие View Model. Надо признать, что идея не совсем оправдала себя. Подходы к организации пользовательского интерфейса в iOS и Windows оказались настолько различны, что пришлось писать отдельные View Model для Windows и iOS. Чтобы как-то шарить код между ними, пришлось очень широко использовать паттерн Command. Такой код, как валидация, вызовы сервисов, вызовы бизнес логики, подготовка параметров и обработка результатов — переместился в классы реализации ICommand, которые повторно использовались в разных ViewModel. Последний же представлял собой тонкую прослойку между Model и View, для биндинга контролов, использующую набор Command.

Конкретный пример: пользователь выбирал в списке товар и назначал скидку. В Windows в соответствии с UX guidelines выбор и применение скидки делался на другой странице. В iOS появлялось всплывающее окно на той же странице. Применение скидки к товару находилось в классе ApplyDiscountCommand. ViewModel для Windows открывало новую страницу, где и вызывалась данная команда. ViewModel для iOS открывало всплывающее окошко, где пользователь выбирал скидку и ApplyDiscountCommand вызывалась в этой же ViewModel.

Работа с MVVM в iOS оказалась неудобной и не естественной для данной платформы. В частности приходилось все биндинги прописывать в коде вручную, а вызывать команды также в коде с помощью Execute(). Использование конвертеров также было неудобным.

Также неприятным моментом было отсутствие полной поддержки System.Xml.Linq в iOS. Пришлось ограничиться устаревшими классами из пространства имён System.Xml: XmlDocument, XmlElement, XmlAttribute.

Мы использовали Xamarin совместно с фреймворком MvvmCross. Он в целом мне понравился. Логичная и понятная поддержка Mvvm, легковесные реализации таких вещей, как IoC, Messenger, куча кроссплатформенных плагинов, например, работа с Http. Недоразумение вызывает лишь реализация передачи параметров во время навигации на другую страницу. Для этого требуется, чтобы класс параметров состоял из свойств простых типов (int, bool, string), т.к. он потом сериализуется в URL. Удивительное решение сериализовывать что-то, что передаётся в рамках одного процесса и потока! Данную библиотеку могу смело рекомендовать как кросс-платформенный MVVM фреймворк.

Подводные камни Xamarin Forms


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

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

Когда мы только начали работать с Xamarin.Forms, большим разочарованием стало малое количество контролов. Мы были шокированы, узнав, что следующих контролов нет по умолчанию в Xamarin.Forms:
  • RadioButton
  • CheckBox
  • Hyperlink

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

Конечно, можно было использовать кастомные рендереры этих контролов, но мы принципиально не хотели заморачиваться с написанием кода под разные платформы, поэтому мы написали свои версии этих контролов полностью кроссплатформенно! Мы использовали примитивные контролы XF, включая BoxView и Line, чтобы собрать из них нужные нам. В Андроид эти контролы не выглядят чужеродно, особенно в контексте общей стилистики.

image

Ещё один шок: отсутствие свойства MaxLength у текстового поля (Entry). Задавать максимальную длину текста предлагается с помощью механизма Behaviors или просто, отсекая лишнее в OnTextChanged. Почему XF не может добавить такое базовое свойство и заставляет меня писать самому свой контрол? Вопрос риторический.

Также не у всех контролов есть bindable свойства, например у DatePicker и TimePicker.

Доставляют мелкие всплывающие косячки, например, приложение периодически падает, когда к одному свойству биндятся несколько контролов. Только на выявление источника этого трудновоспроизводимого бага одному из наших разработчиков потребовалось полтора дня! Пришлось создавать свойство-копию и биндиться к разным свойствам, тогда падения прекратились.

Поддержка жестов и сложного интерфейса достаточно отсталая. У нас есть страница, позволяющая выбирать время быстрее, чем стандартный TimePicker. Она сделана в виде аналоговых часов, где пользователь может двигать стрелки в разные стороны и выбирать часы и минуты. Увы, её не получилось реализовать на Xamarin Forms и это единственная страница в нашем приложении, которая написана нативно. По сути, в Xamarin Forms доступно только два жеста: нажатие и двойное нажатие. Такие жесты как долгое нажатие, проведение пальцем влево/вправо не поддерживаются.

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

Тем не менее, мне очень нравится, что в Forms есть полноценная поддержка стилей и темплейтов. Я считаю её очень сильной стороной в XAML, и хорошо, что в Xamarin это присутствует. Также один из приятных моментов — это мощные возможности XAML, которые, конечно, отстают от WPF, но примерно одинаковы с XAML для Windows Store-приложений. Разработчикам, привыкшим работать с XAML, будет достаточно комфортно в Xamarin Forms. Биндинги, команды, конвертеры – всё это поддерживается и можно полноценно использовать MVVM. Принимая во внимание, конечно, убогие контролы и разные глюки, как в вышеописанном примере, но которые постепенно исправляются.

Xamarin развивается, часто идут обновления. Например, у нас был баг: при частом многократном нажатии на список приложение падало. Мы сразу решили, что это баг Xamarin, и что не будем тратить время на исправление, а подождём, может Xamarin сам это исправит. И действительно, через 2-3 месяца с очередным обновлением баг ушёл.

Какова среда разработки


Xamarin сильно испортил Visual Studio, эту в общем то великолепную среду разработки, славящуюся своим удобством и надёжностью. Нет-нет, мне Майкрософт не платил за рекламу «вижуалки», просто тут случай как в поговорке: что имеем — не ценим, потерявши — плачем. Ниже я перечислю основные трудности, которые у нас возникли в первую очередь с инструментарием.

Visual Studio с плагином Xamarin часто виснет, зачастую предсказуемо. Например, периодически виснет во время редактирования XAML. Обходное решение, найденное на просторах рунета – удалить файл suo (пользовательские настройки проекта), тогда можно ещё какое-то время редактировать разметку. Но на следующий день опять зависнет. Иногда зависает до трёх раз на дню, иногда радует «надёжностью» целый рабочий день.

Также ни с того, ни с сего проект может перестать деплоиться на телефон. Вот секунду назад запускал и всё было ОК, а теперь Package Deployment Failed. Открываешь Xamarin Studio, запускаешь там, оно спокойно деплоится, потом возвращаешься обратно к VS и продолжаешь работать. Иногда бывает, что-то самозабвенно программируешь, раз — и зависло. Дабы не прерывать полёт мысли, не моргнув глазом переключаешься на XS и продолжаешь там, благо можно настроить ту же цветовую схему редактора, что и в VS. XS в целом надёжнее, но работать всегда там не получается, т.к. нет поддержки TFS. Так что рано или поздно приходиться возвращаться на VS.

Ещё одним неприятным моментом было то, что в Xamarin-проектах перестал работать Ghost Doc (он не поддерживает shproj-проекты). А на XS он не ставится. Хорошо хоть StyleCop для XS имеется.

Большим недостатком я считаю тот факт, что VS лишилась возможности адекватно отображать необработанные исключения. Теперь не показывается стандартное окно с информацией об исключении и месте, где оно произошло. Появляется окно, как на скриншоте, а Stack Trace и место, где упало, приходится смотреть в Output window, что неудобно. В XS всё немного получше, но тоже неудобно.

image

Также, в VS при открытии XAML файла, всегда пытается загрузиться и дизайнер в пол экрана. Сделать он это не в состоянии даже в теории и приходится его закрывать. Что опять-таки лишние манипуляции мышью. К сожалению, он не запоминает последний использованный вид редактирования. Как отключить загрузку дизайнера я нигде не нашёл. В XS с этим проблем нет, сразу открывается код.

IntelliSense в VS живёт своей жизнью: работает и отключается по своим неведомым правилам, причём перезагрузка VS помогает не всегда. Помимо этого, не видит только что добавленных классов. Зачастую добавление нового класса и ломает IntelliSense. В XS, на мой взгляд, IntelliSense реализована хуже, но работает стабильно.

Все эти недостатки снижают продуктивность и на фоне того, что XS стабильнее, в очередном порыве негодования наталкивают на параноидальные мысли о глобальном заговоре: как будто таким образом Xamarin вынуждает отказываться от VS в пользу XS.

Всё лето я мучил Гугл вопросами о том, когда же выйдет VS 2015, ведь там поддержка Xamarin уже встроенная. Наконец, дождавшись официального релиза, я скорее запустил в нём наш проект. Майкрософт как всегда остался верен сам себе, чтобы попробовать новые фичи, нужно установить их последнюю операционку. VS 2015 работает в семёрке, но чтобы попробовать собрать Shared проект Xamarin нужна как минимум Widows 8.1.

Таким образом, если вы начинаете проект с Xamarin, советую работать с Windows 8.1 + VS2015. Если вы ещё сидите на семёрке и на 8.1 никак не перейти, рассмотрите возможность работать только с Xamarin Studio, используя git или SVN, но не TFS.

Xamarin часто ставили в вину, что у них недостаточно документации и статей в интернете, мало вопросов на stackoverflow. Пожалуй, сейчас всё намного лучше и уже достаточно много вопросов как на stackoverflow так и на Xamarin Forums.

Заключение


В заключении дадим некоторые главные выводы из материала:

  • Xamarin и Xamarin.Forms вполне можно использовать на крупных проектах, там где нужно использовать один и тот же код на разных платформах. Отвечая на статью годичной давности «Xamarin.Forms не готов к боевым условиям?» уверенно могу ответить, что готов, но требует определённого допиливания.
  • Будьте готовы к борьбе с утечками памяти, особенно если вы разрабатываете под iOS.
  • Один из главных советов: всегда старайтесь использовать тип проекта PCL для общего кода.
  • Если вы не используете Xamarin.Forms, нет большого смысла использовать паттерн MVVM, он заточен только под XAML. Для Xamarin.Forms могу рекомендовать фреймворк MVVMCross, он вполне неплохо справляется со своими обязанностями и постоянно совершенствуется.
  • Если имеется выбор между Visual Studio 2013 и 2015, то выбирайте 2015, если конечно вашей ОС является Windows 8.1+. Если вы ещё работает на семёрке, то можно использовать Xamarin Studio, но тогда уж лучше не использовать TFS, чтобы не привязывать себя к Visual Studio.

Автор artur_g
Tags:
Hubs:
+12
Comments 35
Comments Comments 35

Articles

Information

Website
icl.ru
Registered
Founded
Employees
1,001–5,000 employees
Location
Россия