Хочу поделиться опытом разработки с использованием Xamarin (звучит как Замарин) на двух крупных проектах. Первый проект был под Windows Store и iOS, второй только под Андроид, но c использованием Xamarin.Forms. Xamarin быстро развивается, поэтому некоторые описываемые здесь моменты, могли уже стать неактуальными. Например: ещё летом мы переживали из-за дикого потребления памяти в Андроид и даже вручную вызывали сборщик мусора в некоторых местах, но в конце лета вышло обновление, которое закрыло многие наши проблемы с памятью.
Не хочу показаться Капитаном Очевидность для некоторых, но повторю, то, что уже неоднократно обсуждалось на Хабре и различных форумах: нет никакого смысла использовать Xamarin в «обычных» мобильных приложениях. Под «обычными» мобильными приложениями я понимаю приложения, обладающие следующими характеристиками:
Здесь я не буду затрагивать огромный пласт игр, это свой мир.
Основной мотивацией использовать Xamarin должно быть наличие большого объёма кода, который работает на разных платформах и проще, когда он написан на одном языке, используя кроссплатформенный фреймворк. Мы писали приложение в области розничной торговли под Windows и iOS. В этом проекте только расчет цены товара занимал несколько тысяч строк кода.
Xamarin.Forms – часть Xamarin, которая позволяет кроссплатформенно создавать пользовательский интерфейс с помощью XAML. Можно использовать Xamarin без Xamarin.Forms, создавая интерфейс с помощью классов-обёрток над нативными классами. Xamarin без Forms нельзя использовать для кросс-платформенного создания пользовательского интерфейса.
Выбор между Xamarin и Xamarin.Forms в документации предлагается делать по следующей схеме.
В Xamarin есть два способа создать проект в солюшене:
Первый – реализация Xamarin, аналог shared project в Windows Store-приложениях, когда проект не компилируется в отдельную сборку, а как бы встраивается в основной проект, в котором запускается приложение.
Главным уроком для нас стало то, что нет никаких причин использовать замариновский тип проекта 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. Может возникнуть аналогичная ситуация, когда ни тот, ни другой не смогут удалить неиспользуемые ресурсы. Тем не менее, после установки последних обновлений, при работе с пользовательским интерфейсом, контролами, значительных утечек мы не заметили. По мелочи же, всегда что-то происходит, пытаться выяснить глубинные причины, в условиях, когда следующее обновление может всё поменять, не разумно. Поэтому во всех наших страницах, при переходе на следующую страницу мы вызываем такой некрасивый код:
При навигации на новую страницу мы создаём её экземпляр заново. Такой способ позволяет бороться со сложно отлаживаемыми утечками памяти, внезапными беспричинными падениями и т.п. Кого-то такой способ может шокировать, однако, если вы посмотрите форумы и блоги, посвященные проблемам памяти, то поймёте, что в мире Xamarin — это нормально. При этом страдает производительность, но существенных, видимых глазу задержек при открытии страниц не происходит.
В проекте под 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, должно иметь следующую мотивацию: очень много экранных форм и нежелание поддерживать 3 версии под разные платформы.
Часто слышал, якобы это маркетинговый миф, что используя Xamarin Forms, не придётся рано или поздно осваивать нативную разработку. Наш пример показывает, что это не миф и даже разработчики, не знакомые с тем, как создавать нативные интерфейсы, успешно справляются с задачами.
Когда мы только начали работать с Xamarin.Forms, большим разочарованием стало малое количество контролов. Мы были шокированы, узнав, что следующих контролов нет по умолчанию в Xamarin.Forms:
Их отсутствие объясняется тем, что в iOS таких контролов нет, и для схожих задач используются другие визуальные элементы.
Конечно, можно было использовать кастомные рендереры этих контролов, но мы принципиально не хотели заморачиваться с написанием кода под разные платформы, поэтому мы написали свои версии этих контролов полностью кроссплатформенно! Мы использовали примитивные контролы XF, включая BoxView и Line, чтобы собрать из них нужные нам. В Андроид эти контролы не выглядят чужеродно, особенно в контексте общей стилистики.
Ещё один шок: отсутствие свойства 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 всё немного получше, но тоже неудобно.
Также, в 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.
В заключении дадим некоторые главные выводы из материала:
Автор artur_g
Когда использовать Xamarin и Xamarin.Forms
Не хочу показаться Капитаном Очевидность для некоторых, но повторю, то, что уже неоднократно обсуждалось на Хабре и различных форумах: нет никакого смысла использовать Xamarin в «обычных» мобильных приложениях. Под «обычными» мобильными приложениями я понимаю приложения, обладающие следующими характеристиками:
- содержат немного бизнес-логики;
- вся бизнес-логика реализована на сервере;
- главным в приложении является удобный пользовательский интерфейс.
Здесь я не буду затрагивать огромный пласт игр, это свой мир.
Основной мотивацией использовать Xamarin должно быть наличие большого объёма кода, который работает на разных платформах и проще, когда он написан на одном языке, используя кроссплатформенный фреймворк. Мы писали приложение в области розничной торговли под Windows и iOS. В этом проекте только расчет цены товара занимал несколько тысяч строк кода.
Xamarin.Forms – часть Xamarin, которая позволяет кроссплатформенно создавать пользовательский интерфейс с помощью XAML. Можно использовать Xamarin без Xamarin.Forms, создавая интерфейс с помощью классов-обёрток над нативными классами. Xamarin без Forms нельзя использовать для кросс-платформенного создания пользовательского интерфейса.
Выбор между Xamarin и Xamarin.Forms в документации предлагается делать по следующей схеме.
Какой тип проекта выбрать
В Xamarin есть два способа создать проект в солюшене:
- Shared Project
- PCL Library.
Первый – реализация Xamarin, аналог shared project в Windows Store-приложениях, когда проект не компилируется в отдельную сборку, а как бы встраивается в основной проект, в котором запускается приложение.
Главным уроком для нас стало то, что нет никаких причин использовать замариновский тип проекта 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, чтобы собрать из них нужные нам. В Андроид эти контролы не выглядят чужеродно, особенно в контексте общей стилистики.
Ещё один шок: отсутствие свойства 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 всё немного получше, но тоже неудобно.
Также, в 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
Don_Eric
мы уже давно используем Xamarin и в этом году выпустили приложение и на Forms.
со статьей полностью согласен, кроме нескольких моментов:
* даже в обычных приложениях удобно использовать Forms. Приходится писать больше нативных рендереров, но в конце концов это окупается в плане поддержки.
* MvvmCross я б не рекомендовал использовать вместе с Forms. Если для стандартных Xamarin (iOS/Android) он необходим, то для Forms больше минусов чем плюсов. Сам проект (MvvmCross) стал развиваться гораздо медленнее, влияет на время загрузки аппликации, а плюшки как биндинг, IoC, messenger встроены в сам Forms фреймворк
Don_Eric
IoC нет, но возможность регистрировать и вызывать сервисы — да
artur_g
А что можно было бы использовать вместо MvvmCross? MVVMLight — вроде как не кроссплатформенный, по крайней мере был, на момент начала проекта. Caliburn.Micro лично мне не очень нравится. Использовать MVVM без MVVM-фреймворка значит самому написать MVVM-фреймворк в процессе написания приложения.
Don_Eric
а что именно вам нужно в фреймворке? Xamarin Form он и есть сам по себе Mvvm-фреймворк — с байндингами, навигацией, dependency service, messaging service.
Кстати, тут замечательный блог с конкретными примерами:
adventuresinxamarinforms.com
artur_g
dependency service в xamarin это всё-таки не совсем IOC+DI. он в документации позиционируется как способ вызывать нативный код из общего кода. как он себя поведёт при большом количестве зависимостей? можно ли с помощью него зарегистрировать синглтон? сможет ли он создавать объекты, где зависимости инжектятся через конструктор? думаю ответ на все вопросы — нет.
от фреймворка мне нужно, чтобы он автоматически биндил View и ViewModel, и создавал ViewModel и вообще сам заботился о его жизненном цикле. чтобы он умел передавать параметры из одной ViewModel в другую. чтобы он умел показывать сообщения и ошибки из ViewModel во View в соответствии с паттерном, ещё я хотел бы мощную реализацию интерфейса ICommand, чтобы была возможность использовать генерики, параметры и асинхронность. Также хотелось бы всяких приятых плюшек, например готовые конвертеры на разные случае жизни, поддержку настроек, локалиации и т.п… Всё это можно реализовать самому или найти на просторах инета, но хотелось бы чтобы всё это было в одном месте, чтобы фреймворк поддерживался и развивался.
VVS0205
Вы можете использовать MugenMvvmToolkit, проект полностью кроссплатформенный и поддерживает все основные мобильные платформы, у него много плюсов в сравнении с MVVMCross, например:
Используя MugenMvvmToolkit вы можете передавать любые параметры, потому что вы сами создаете ViewModel:
Don_Eric
Тогда наверно Prism, part of the .NET Foundation. Один из самых популярных фреймворков для WPF, в этом году они перешли на open-source и добавили поддержку Forms. Использует Unity и Ninject
brianlagunas.com/first-look-at-the-prism-for-xamarin-forms-preview
github.com/PrismLibrary/Prism
ad1Dima
MVVMLight уже работает с Xamarin (cам не использовал)
в Caliburn вроде ещё не допилили поддержку
ad1Dima
Это касается только Forms. Нативные средства построения интерфейсов никуда не делись-то.
ad1Dima
С другой стороны PCL сильно ограничен и в .NET функциях. А в shared Project можно использовать условную компиляцию. К тому же VS2015 подсказывает, какой код не будет работать в других проектах, к которым подключён Shared
artur_g
Ещё один аргумент перехода на VS2015 и Win 8.1+ :)
Nagg
Спасибо, повеселило :-).
vovtil
проще написать два нативных приложения на родных средах чем все эти пляски с бубном
artur_g
а например если у вас много общей логики (десятки тысяч строк кода), то будете один и тот же код реализовывать на разных языках, а потом поддерживать две версии одного и того же?
silentnuke
Да, ибо в итоге это всеравно будет проще.
ad1Dima
Чем? У нас на одном реальном проекте до 75% общего кода. UI нативный для каждой платформы. Новые фичи добавлять получается быстрее, чем трижды на 3 платформы.
silentnuke
Разбор багов/тюнинг платформы хмарин в итоге выйдет намного дольше, чем написание на двух платформах и поддержка их.
Видел, как довольная известная специализирующася компания на разработке xamarin, потратила кучу времени (несколько лет) на разработку продукта и в итоге так и не смогла ее нормально довести до конца. И да там много общей бизнес лоигики.
Время затраченное на xamarin в итоге будет больше, чем посадить комманды под разные платформы. Потому что найти много хороших специалистов, которые в нем разбираются будет намного сложнее, чем найти хороших спецов под кондкретную платформу.
Какое-то простое приложение, да будет проще написать на Xamarin. Но что-то довольно сложное, например Snapchat, однозначно нет.
Nagg
У вас есть опыт в этом деле или всё ограничивается «Видел, как довольная известная компания»? Сколько уже я диванных аналитиков повидал.
silentnuke
Конечно же нет реального опыта, пришел тут семечки полузгать ;-)
Nagg
Я так и думал. Андроид разработчика можно очень быстро научить писать на Xamarin. Фактически он будет писать как на яве слой интерфейса — теже классы, теже инструменты для UI (более того — можно использовать Android Studio для дизайна). Но при этом они получают два плюса: 1) коровая логика становится доступной для всех других платформ. 2) они получают мощный язык с лямбдами, свойствами (и ещё 20 пунктов по которым ява отстала). Ну и всего-то надо немного понимать как оно работает дабы не вылезть за грефы — для этого достаточно часок-два потратить на чтение пары статей на developer.xamarin.com/guides/android/under_the_hood/architecture
ad1Dima
Чем написание — возможно, потому что надо думать, когда пишешь кросс-платформенный код. Поддержка — не факт.
Бизнес-логика у снепчата не слишком-то сложная. вот UI-да. Выйгрыша от Xamarin почти не получится. Хотя написание UI не будет принципиально отличаться от нативного.
Don_Eric
иногда больше чем 2 приложения. У нас один и тот же код бежит для iOS, Android, Windows Store, mobile web и десктопные симуляторы/тесты
om2804
Xamarin Forms на самом деле сыроват до сих пор. Куча багов, которые фиксятся месяцами. Новых контролов не появляется. Да чего там. Даже старые неохотно расширяются новыми свойствами. У меня уже целая куча кастом рендеров, которые правят баги и расширяют контролы.
Сейчас это больше усреднение между платформами. Если фишки нет в какой-либо платформы, её нет в Forms.
Мне только одно не понятно. Откуда такие ценники на этот продукт?
А работоспособной бесплатной версии-то и нет
Nagg
За это я и не люблю Xamarin.Forms — все теперь судят замарин только по нему, и да он не идеален и поэтому хорошенько портит так карму компании. А так XF — это всего лишь фраемворк, который пилит небольшая команда.
QtRoS
Хм, а рекламируется повсеместно как готовое и полноценное средство, между прочим… В таком случае я бы Qt выбрал, там ведь по-настоящему кроссплатформенный C++ и QML, никаких проблем с обработчиками и многопоточностью. Единственная грусть — неполная возможность контроля жизненного цикла приложения (но при ровных руках можно допилить класс активити из фреймворка и будет счастье). Но с другой стороны надо признать, что на C# многие вещи пишутся быстрее, чем даже на C++\Qt (для тех кто не знаком с Qt — там С++ немного другой из-за MOC).
Nagg
А я бы выбрал классический замарин. В нем можно и логику написать на шарпе и в платформенном коде заюзать любой контрол из pods или jcenter/maven. А у вас есть примеры известных приложений на Qt, а то много раз слышал как люди собирались идти в мобайл с кутэ, но ни разу не видел.
ad1Dima
а как именно вы интегрируете любой контрол из pods или jcenter/maven?
Nagg
Objective Sharpie 3.0 умеет в pods уже.
Jcenter/maven — через мой плигиньчик к студии :-).
ad1Dima
Напишите статью про это, чтоб в противовес Xamarin.Forms повсеместному
silentnuke
2gis — QT.
А у вас есть примеры известных приложений на Xamarin?
ad1Dima
UI у них нативный. А Qt они допиливали сначала на Android, потом на iOS потому что у них WinMo6 gриложение на Qt было написано.
Эльба вон недавно написали. Они вообще на Xamarin.Forms (вот жеж делать нечего).
ad1Dima
А так, на сайте есть у них примеры xamarin.com/customers