Xamarin и Xamarin.Forms – кактус в шоколаде. Часть 1

в 12:20, , рубрики: xamarin, xamarin.android, xamarin.forms, xamarin.ios, Блог компании СКБ Контур, разработка мобильных приложений, Разработка под android, разработка под iOS, Эльба

Xamarin и Xamarin.Forms – кактус в шоколаде. Часть 1 - 1Мы в Контур.Эльбе обожаем мобильные приложения. У нас уже был опыт написания приложения под iOS, а также разработки и дальнейшей поддержки приложения под Android. В этом году мы вновь выпустили версию под iOS, но на этот раз на базе Xamarin и Xamarin.Forms, и нам не терпится поделиться опытом. Пока что мы успели рассмотреть разработку только под iOS, но впечатлений уже море, да и про Android пару слов однозначно скажем.

Что такое Xamarin

Фреймворк для кроссплатформенной разработки мобильных приложений на базе .NET (точнее, на реализации Mono), поддерживает все основные ОС — Android, iOS и Windows Phone. Подробнее на Хабре писалось неоднократно, повторяться нет нужды.

Что такое Xamarin.Forms

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

Почему мы выбрали именно Xamarin+Xamarin.Forms?

Причин было несколько.

  1. На сервере мы используем .NET и C#, поэтому довольно удобно использовать один язык и платформу для трёх приложений (сервер, iOS, Android) – нет необходимости переключаться с одного языка на другой, возможность использовать привычные паттерны, легко выделять общие фрагменты в библиотеки и т.п.
  2. Из доступных фреймворков Xamarin выглядел самым развитым.
  3. Вариант создания HTML5-приложения мы не рассматривали: в приложении планировалось множество форм, а нативные компоненты в этом плане работаю заметно предсказуемее и быстрее. Особенно актуально для недорогих Android-смартфонов.
  4. Xamarin.Forms порадовал потенциальной возможностью сделать с минимальными допилками общий интерфейс для Android и iOS, а также нёс ряд плюсов, например – привычную для нас блочную модель вёрстки на iOS.

Первая кровь

Начало общения с платной версии началось с косяка системы активации: в личном кабинет на сайте Xamarin уже было видно, что оплата (999$ или 799$ для подписчиков MSDN) произведена, а вот Xamarin.Studio упорно отказывалась билдить приложение, т.к. “платная версия не активирована”. Помогла только ручная активация оплаты технической поддержкой Xamarin. Ответили они быстро, но в любом случае это отличное начало, задавшее тон всему последующему проекту.

К слову, бесплатная ознакомительная версия не позволяет собрать приложение размером более 64 килобайт для скомпилированного кода. То есть подключаем, скажем, тот же json-сериализатор (Newtonsoft) – и собрать приложение уже нельзя. Мило.

Проблемы с IDE

Xamarin предоставляет собственную IDE Xamarin.Studio, основанную на базе MonoDevelop. К сожалению, эта IDE имеет ряд проблем:

  1. Нестабильность. Например, очень высок шанс зависания намертво при попытке прервать сборку приложения.
  2. Периодически ломается подсветка или автокомплит.
  3. Автодополнение не умеет искать классы, не входящие в текущее пространство имён, хотя по полностью написанному обращению к классу нужный using среда прописать может.
  4. Возможностей для рефакторинга явно не хватает, до Resharper’а ей в этом плане далеко.
  5. Периодически ломается интеграция с редактором XIB’ов (файлы вёрстки для iOS). Если вы используете вёрстку через XIB или Storyboard, то для редактирования открывается XCode, а по закрытию его – обновляются автосгенерированные классы, связанные с UI. Но не всегда. Иногда среда в принципе перестаёт вызывать XCode для некоторых или вообще всех файлов. Лечится очисткой кэша студии (удалением содержимого директории ~/Library/Caches/XamarinStudio-5.0).

Но есть и хорошие новости! На тарифе business есть интеграция с Visual Studio, позволяющая писать код там. Работает очень хорошо, с частью проблем, например такими, мы не сталкивались, видимо потому, что пока не работали с Android и XAML. Но зато столкнулись с другими.

Дело в том, для сборки iOS приложения нужен компьютер или виртуалка с Mac OS X. И тут у вас два варианта:

  • собирать и отлаживать приложение из Xamarin Studio, запущенной на маке;
  • использовать плагин для интеграции между Visual Studio и маком (билд-хостом), тогда процесс сборки и запуска на симуляторе можно запустить из VS, и там же отлаживать. Но картинка симулятора, если вы запускаете приложение на нём, всё равно рисуется только на Маке, так что вам придётся сидеть рядом с двумя ПК разом, тестируя приложение на одном и работая в отладчике на другом.

Более того, плагин для интеграции долгое время работал нестабильно. У нас периодически возникала такая ситуация: три разработчика, у всех одна и та же версия ОС, студии, Xamarin'а и так далее. Подключаются по очереди к одному и тому же маку. В результате у одного всё работает нормально, у второго игнорируются break-point'ы, а у третьего не работает совсем.

Техническая поддержка посоветовала попробовать последние 4 версии под Mac и Windows, “может быть какое-нибудь сочетание заработает”. Установка занимает минут 20-30, т.к. инсталлятор скачивает даже те компоненты (например, библиотеки Android SDK), которые уже скачивал прошлый раз. Вот и думайте…

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

Поэтому мы долгое время работали так:

  1. Код лежит в сетевой папке, доступной на Mac’е и в запущенной на нём же виртуальной машине Parallels с Windows;
  2. Основная часть кода пишется в Visual Studio под Windows внутри этой виртуалки;
  3. Отладка или сборка финальной версии происходит в Xamarin.Studio на Mac OS X, но не через плагин, а через ручное открытие того же кода в общей папке.

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

эта зараза даже стандартные Enter/Escape не понимает!

К счастью, после многих месяцев мучений, команда Xamarin сжалилась над нами. Последнее обновление Xamarin.Studio эту проблему решила – теперь в окошке подключения к удаленному маку появилась галочка “не показывать окно подключения”.

А, ну и ещё удаленный билд теперь делает не через собственную Xamarin’овскую утилиту, а через обычных удаленных пользователей на Mac OS X:

Xamarin и Xamarin.Forms – кактус в шоколаде. Часть 1 - 3

Как результат — соединение стало довольно стабильным и отладка нормально запускается в 8 случаях из 10. Но в любом случае симулятор запускается на другом ПК, т.е. дебаг идёт на вашей машине, а приложение запущено где-то там, картинка не выводится на вашем ПК. Можно открыть удалённый сеанс или держать второй компьютер на том же столе.

Решение всё равно остаётся несколько костыльным, но оно позволяет полностью отказаться от использования Xamarin Studio! Поверьте, после нескольких месяцев жизни в ней можем сказать однозначно: оно того стоит.

Есть проблемы и с профайлингом. Xamarin.Profiler (GUI обёртка над Mono log profiler) для iOS приложения долгое время не показывал названия написанных вами классов, была только информация, что процессорное время и память занимают такие-то нативные объекты, но как их сопоставить с написанными вами классами — ни капли не очевидно. Сейчас с этим дела обстоят лучше – написанные классы наконец то появились в списке.

Также под профайлером приложение работает намного медленней, и, как следствие, оно порой может просто не успеть за отведённое на запуск время показать первый экран, и iOS его прибьёт. А если приложение таки запустится, то может вылететь в процессе работы на симуляторе под профайлером. В качестве слабого оправдания можно заметить, что профайлер уже давно находится на стадии альфа-версии, и за последний год так и не смог добраться даже до стадии “стабильной беты” – по крайней мере, для iOS.

Xamarin и Xamarin.Forms – кактус в шоколаде. Часть 1 - 4

Проблемы с кроссплатформенным кодом

Писать кроссплатформенный код нам предлагают одним из двух способов:

  1. Shared Project. Код в этом проекте не компилируется в отдельную сборку, а просто включает себя как часть в несколько проектов сразу. Этакий проект-симлинк.
  2. PCL Library. (Portable Class Library) Кроссплатформенная библиотека.

В теории вам предлагается выбрать что-то одно. На практике мы задействовали оба эти метода.

В PCL-проекте мы писали всю бизнес-логику, работу с БД, сетью и т.п. Основной плюс в том, что PCL не позволит использовать какой-нибудь системный класс, не реализованный на одной из платформ. Так что написанный и собранный в рамках PCL код гарантированно запустится на всех выбранных платформах.

Минус в том, что PCL-библиотека пишется исходя из пересечения возможностей всех выбранных ОС, не позволяя использовать то, что отсутствует в одной из них. Т.е., если мы планируем использовать только iOS и Android, а какой-то нужной фичи нет в Windows Phone – мы ей не сможем воспользоваться. При этом для PCL есть набор “профилей”, определяющих поддерживаемые платформы, но на момент написания статьи выбрать вариант “только iOS и Android, без Windows Phone и Windows” невозможно.

Xamarin и Xamarin.Forms – кактус в шоколаде. Часть 1 - 5

В результате мы помещали в shared-сборку код, гарантированно работающий на iOS и Android, но не способный собраться под PCL. Например, туда попала поддержка gzip-ответов и другая специфика работы с сетью или базой

В PCL может не быть банальных вещей, вроде работы с файлами и т.п. — в этом случае предполагается, что в PCL-сборке будут интерфейсы, а реализации — уже в проекте под конкретную ОС. В нашем случае часть таких реализаций как раз-таки попадало в shared-сборку.

К слову, если выбираете профиль для PCL – советуем выбрать профиль 259. Подходит лучше всех по имеющимся возможностям. Мы начали на другом, не имеющем в своём составе, например, мьютексов или потокобезопасных словарей — написать руками можно, но зачем? Так же отмечу, что выбор профиля гораздо удобнее и нагляднее делается в Xamarin.Studio, а не в Visual Studio:

Xamarin и Xamarin.Forms – кактус в шоколаде. Часть 1 - 6

Ну и на закуску — ещё одна особенность PCL: вы не можете добавить в зависимости одной PCL-библиотеки другую PCL-библиотеку с иным профилем. Точнее, добавить то можете, но не любой профиль уживётся с любым. В общем, идея PCL в целом юзабельна, но в частностях может довести до белого каления.

Проблемы с биндингами к нативным возможностям платформ

Базовая идея Xamarin в том, что вы можете писать код, используя те же классы, методы и структуры, что использовали бы при написании нативного кода, просто теперь он пишется на C#. Но есть нюансы…

В случае с iOS существует довольно занятная багофича с делегатами. Вот, скажем, компонент UIScrollView. Ему можно назначить либо делегат, обрабатывающий определённые события, либо подписаться на эти события напрямую.

var scrollView = new UIScrollView();

//вариант 1
class UIScrollViewDelegate1: UIScrollViewDelegate
{
    public override void ScrollAnimationEnded(UIScrollView scrollView)
    {
        base.ScrollAnimationEnded(scrollView);
        Debug.WriteLine("Yes, animation ended");
    }
}
scrollView.Delegate = new UIScrollViewDelegate1();

//вариант 2
scrollView.ScrolledToTop += (_, __) => { Debug.WriteLine("Yes, scrolled to top"); };
scrollView.ScrollAnimationEnded += (_, __) => { Debug.WriteLine("Yes, animation ended"); };

Причем при подписке на событие ScrollAnimationEnded происходит автоматическая замена делегата на внутренний Xamarin’овский. И подписка на событие ScrolledToTop будет потеряна. Казалось бы – в чем проблема? Просто не смешивай в одном коде подписку на события и делегаты! Но порой встречаются проблемные ситуации:

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

Или вот, например, в Xamarin.iOS используется класс единый класс UITableViewSource, заменяющий два iOS-протокола UITableViewDataSource и UITableViewDelegate. Это может сбить с толку.

Так же иногда меняются сигнатуры методов и классов, тот же iOS конструктор fileURLWithPath превратился в Xamarin в CreateFileUrl.

Аналогичная ситуация встречается и в Android'е — скажем, в оригинале константа GONE находится в классе View, а в Xamarin’овском варианте для той же цели нас есть enum ViewStates со значением Gone. Подобная проблема имеется и с некоторыми другими методами и константами, что вносит некоторую путаницу при переходе с нативной платформы.

Отсутствие абстракций над платформенными особенностями

Для полного счастья, фреймворк не скрывает от вас часть особенностей конкретной ОС. Например – фоновая работа с сетью:

  1. В Android'е вы можете просто породить новый поток для работы с сетью или фоновной сетевой сервис, если ваше приложение будет свёрнуто или телефон будет залочен — работа не прервётся.
  2. В iOS при сворачивании приложение останавливается, и если вы хотите что-то делать с сетью — нужно использовать иной вариант, например Background-Safe Task, останавливающуюся через несколько минут после сворачивания.

Вы скажете — это ведь просто особенности конкретной платформы? Именно. Но никто от разработчика эти особенности под абстракциями не прячет, так что повторяем, изучать каждую из платформ придётся.

Нехватка сторонних компонентов

В данный момент под Xamarin уже написано немало компонентов, но под iOS и Android их всё-таки написано больше. Если понадобится, то теоретически можно подключить бинарную библиотеку, написанную на Object-C или Java, но придётся писать проект-биндинг для связи между нативной библиотекой и Xamarin-приложением. Для iOS здорово поможет утилита Objective Sharpie, способная самостоятельно сгенерировать такой проект, но она тоже не всегда работает на 100% точно, приходится порой редактировать результат такой генерации руками, а местами даже задуматься, нужна ли вам данная библиотека вообще.

К тому же, подобные компоненты авторы часто обновляют с запозданием, по сравнению с нативной версии библиотеки. Мы стакивались с ситуацией, когда плагин TestFairy отставал на пару месяцев от нативной версии. На вопрос в тех.поддержку TestFairy нам ответили, что всё нормально, вы не теряете ничего, кроме пары мелких фич. Но при выходе новой версии Xamarin возникла проблема: приложения с данной библиотекой собранные в этой версии Xamarin начали падать в реалтайме.

Выводы

После изучения тонкостей фреймворка выводы напрашиваются сами собой.

  • Xamarin позволяет удобно (с некоторыми оговорками) использовать для разработки под мобильные платформы C# и .NET.
  • Xamarin, несмотря на свой возраст, довольно сырой продукт.
  • Xamarin не спасёт вас от необходимости тщательно изучать особенности каждой из мобильных платформ.

В статье также планировался рассказ про Xamarin.Forms, но тот раздел настолько растянулся, что мы решили выделить его в отдельную статью. В ней мы расскажем про различные проблемы UI-фреймвока Xamarin.Forms и о том, как мы их решили. Ну а пока – ставьте лайки и подписывайтесь на блог! Хотя нет, нафиг штамп…

Автор: СКБ Контур

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js