Сетевая недокроссплатформенность

в 6:02, , рубрики: .net, android, iOS, monodroid, monotouch, Блог компании Positive Technologies, информационная безопасность, метки: , , , ,

Сетевая недокроссплатформенность

Здравствуйте! В этой статье я хотел бы поделиться своим опытом с начинающими разработчиками, которые учатся писать мобильные приложения, но еще не очень далеко продвинулись на этом поприще. Если быть точным — я бы хотел рассказать, как писать переносимый код и проектировать приложения, которые будут работать как на «родных» .NET-платформах (Windows Phone 7 и настольные приложения Windows), так и на портированных версиях .NET для мобильных платформ, таких как Monotouch и Monodroid.

Немного о Mono и Portable Class Library

Для того чтобы в деталях разобраться, как правильно писать переносимый код, сначала необходимо понять — как работает .NET на iOS и Android. Тема эта очень большая, поэтому, как говорит Владимир Владимирович, буду краток.

Monotouch

Mono на iOS работает следующим образом: никакого JIT-компилятора и никакого .NET на самой iOS. Весь код, абсолютно весь, в том числе и вся реализация .NET, — компилируется в нативный байт-код и «тянется» с дистрибутивом приложения.

Отсюда вытекают следующие «плюсы, минусы и подводные грабли». Плюс — быстродействие (нативный код по определению быстрее). Минус № 1: объем дистрибутива приложения — минимум 6 Мбайт. Минус № 2: портирован, к сожалению, только профиль Silverlight 4, и то не полностью.

Главная неожиданность: никаких генериков и никакой рефлексии. И следовательно — никаких игр с коллекциями, кастомными прокси-классами для WCF, биндингом данных а-ля WPF.

Monodroid

О .NET на Android нужно сказать следующее: на первый взгляд тут не все так печально: и JIT имеется, и генерики. Однако… Генерики — неполноценные (классы-параметры должны наследоваться от Java.Lang.Object), да и JIT — не совсем JIT. Тут тоже можно выделить тему для отдельной статьи, если Хабрасообществу будет интересна эта тема.

Portable Class Library и Windows Phone 7

Самыми широкими возможностями по использованию C# обладает, разумеется, мобильная платформа от Microsoft. Специально для нее была разработана версия Silverlight. Однако сборки, которые были скомпилированы для Windows Phone — не будут работать на десктопном приложении. Это понятно, хотя и очень неудобно: целевые платформы все-таки разные.

Для адекватной поддержки переносимости кода между Windows Phone и полноценным .NET был реализован профиль Portable Class Library (PCL) — набор базовых классов, которые присутствуют на всех платформах, поддерживающих официальную версию .NET — Windows, Metro, Windows Phone, Silverlight и Xbox.

Нюанс

При разработке под Mono* вы можете прямо ссылаться на сборки, которые были скомпилированы для полноценных версий .NET до 3.5. И если ваша сборка использует только классы из поддерживаемых пространств имен — все отлично сработает. Если же вы попытаетесь обратиться к нереализованному классу, то просто получите исключение NotImplementedException; такова белая магия компилятора Monotouch.

Впрочем, проект для Windows Phone 7 просто не позволит добавить ссылку на подобную сбору (что вполне логично). «А как же Portable Class Library?..» — спросите вы. Вот тут как раз и начинается черная магия: при попытке использовать в проектах Mono* сборки, скомпилированные под PCL или WP7, вы получите сообщение об ошибке: невозможно загрузить сборку, ссылающуюся на библиотеки для WP7 или PCL.

Мне до сих пор непонятно, почему это реализовано именно так; на все вопросы по этому поводу техподдержка отвечает: «Coming soon». Радует лишь, что не так давно вышла новая версия MonoDevelop (среды разработки под Monotouch на Mac), в описании которой было явно указано, что частичная поддержка PCL осуществляется, однако я не смог понять — где именно.

Реализация

Итак, имея реализацию необходимых нам библиотек на всех интересующих нас платформах, мы не можем создать одну сборку для всех платформ, которая бы использовала необходимые классы. Нам придется компилировать две сборки — для Windows-платформ и для Mono-платформ.

Но здесь мы снова сталкиваемся с небольшой проблемой. Самым удобным классом при реализации клиента для веб-сервиса является WebClient. Но его — по каким-то загадочным причинам — из второй редакции PCL убрали, хотя он реализован и на Windows Phone 7, и в настольных приложениях. Разработчики аргументируют это тем, что WebClient не поддерживается в Metro-приложениях. Поэтому если вы хотите разрабатывать приложение в том числе и для Metro — вам придется воспользоваться более низкоуровневыми классами WebHttpRequest и WebHttpResponse. Я же для простоты покажу пример реализации прокси-класса для работы с WebClient.

Что нам потребуется:

  • Visual Studio 2010 SP 1;
  • Windows Phone SDK для Visual Studio 2010;
  • Monodroid для Visual Studio 2010;
  • MonoDevelop 3.0.2 на Mac;
  • Newtonsoft.Json.

Приступаем к работе.

1. Устанавливаем Windows Phone SDK и Monodroid. Создаем два проекта: первый проект — Windows Phone Library, второй — Android Class Library. Если вы захотите разрабатывать универсальную Windows-библиотеку — создайте Portable Class Library вместо Windows Phone Class Library. Но в этом случае для работы с веб-сервисом придется использовать все те же менее удобные HttpWebRequest и HttpWebResponse.

2. Далее основную работу мы будем вести с Android Class Library. Для обеспечения совместимости этой сборки с проектом Monotouch необходимо убедиться, что в этой сборке нет лишних ссылок, специфичных для Monodroid. В проекте оставьте следующие ссылки:

  • System;
  • System.Net;
  • Newtonsoft.Json (библиотека для десериализации JSON).

3. Предположим, что у нас есть некий сферический REST-сервис в вакууме, который расположен по адресу http://webservice:47154/rest/vendor и возвращает нам список производителей ПО.

Сетевая недокроссплатформенность

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

 //Контракт производителя ПО

    public class Vendor
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }


    //Сам прокси-класс для работы с веб-сервисом

    public class VendorsProxy
    {
        private string _host;

        public VendorsProxy(string host)
        {
            _host = host;
        }

        public event VendorsEventHandler GetVendorsCompleted;


        public void GetVendors()
        {
            var client = new WebClient();

            client.DownloadStringCompleted += (s, e) =>
                                                  {
                                                      var vendors = JsonConvert.DeserializeObject<IEnumerable<Vendor>>(e.Result);
                                                      if (GetVendorsCompleted != null) GetVendorsCompleted(new VendorsEventArgs(vendors));
                                                  };
            client.DownloadStringAsync(new Uri(_host + @"rest/vendor"));
        }
    }

    public delegate void VendorsEventHandler(VendorsEventArgs vendors);

    public class VendorsEventArgs : EventArgs
    {
        public IEnumerable<Vendor> Vendors { get; private set; }

        public VendorsEventArgs(IEnumerable<Vendor> vendors)
        {
            Vendors = vendors;
        }
    }

4. Теперь в проект WP Library достаточно добавить не сами файлы, а ссылки на файлы из проекта Android Class Library.

Сетевая недокроссплатформенность
Вот, собственно, и все. Теперь, если вы полностью соберете решение, на выходе окажутся две сборки, которые одинаково хорошо будут работать как в Windows Phone, так и на устройствах с iOS и Android. Вы сможете использовать ваш веб-сервис на любой платформе следующим образом.

var proxy = new VendorsProxy("http://mywebservice:8080/");

            proxy.GetVendorsCompleted += (vendors) =>
            {
                BindVedorsToVoewModel(vendors.Vendors);
            };

            proxy.GetVendors();

Сомнения

Конечно, можно поспорить о необходимости таких «танцев с бубном» для достижения подобной универсальности: наверняка ведь у каждой платформы будет своя специфика и свои оптимальные методы работы. Однако, если вы уверенный .NET-программист и вам необходимо быстро создать простое мобильное приложение, работающее с веб-сервисом, — данное решение может быть полезно.

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

Автор: Сергей Шулик, старший программист Positive Technologies.

Автор: ptsecurity


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


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