Эта статья является частью
серии о моем опыте портирования приложений с Windows Phone на Windows 8. Официальное руководство по портированию можно найти
здесь, и не забывайте следить за
блогом Building Windows 8 и блогом
разработчиков приложений для Windows 8 для получения последней информации.
Если вы в настоящее время работаете над приложением для Windows 8, которое вы хотели бы проверить на предмет скорейшего выхода на рынок, свяжитесь со мной .
В этой статье я расскажу о различиях времени выполнения приложений между Windows Phone и Windows 8. Разработчики Windows Phone уже достаточно комфортно создают приложения Silverlight и, к счастью, эти навыки в равной степени применимы и к Windows 8. Но есть мощный новый фреймворк, называемый Windows Runtime (WinRT), о котором вам нужно знать, а также нам нужно понимать и писать асинхронный код.
WinRT
Когда команда Windows планировала историю разработчиков для Windows 8, им были поставлены сложные задачи. Хотя я не видел реальных спецификаций дизайна, я представляю, что некоторые цели были примерно такими:
- Он должен быть быстрым и плавным (для поддержки дизайна Metro)
- Он должен предоставлять возможности базовой ОС безопасным и простым в использовании способом.
- Он должен быть легко доступен на всех поддерживаемых языках
- Это должно быть естественно для использования на каждом из поддерживаемых языков
- Он должен обеспечивать равные возможности для всех поддерживаемых языков
Это довольно высокие цели, особенно если учесть, что языки, которые Microsoft хотела поддерживать, включают C #, VB, C ++ и JavaScript. Насколько мне известно, не было создано никакой структуры, которая обеспечивала бы равные возможности и была бы естественной для использования на многих языках. Но это именно то, что команда разработчиков сделала, когда создала среду выполнения Windows (WinRT).
Для разработчиков на C ++ WinRT очень похож на библиотеку C ++, которая связана с проектом. Для разработчиков JavaScript WinRT очень похож на внешний файл .js, на который есть ссылки. Для разработчиков на C # и VB WinRT очень похож на .NET Framework. На самом деле это выглядит и выглядит так, как .NET, что, если вы не знаете точно, что искать, вы можете просто предположить, что это так. Это вызвало некоторую путаницу на нашей конференции // build и по-прежнему вызывает у многих разработчиков путаницу, поэтому я хотел бы немного рассказать об этом для тех из вас, кто работает с Windows Phone. На самом деле WinRT ближе всего к тому, что видит разработчик C ++. WinRT — это среда выполнения, созданная командой Windows, написанная на C ++ и доступная для всех четырех языков естественным образом. Это, вероятно, проще всего понять, посмотрев пример кода (или три).
ImageEncodingProperties^ imageProperties = ref new ImageEncodingProperties(); imageProperties->Subtype = "JPEG"; imageProperties->Width = 320; imageProperties->Height = 240; auto opCapturePhoto = m_mediaCaptureMgr->CapturePhotoToStorageFileAsync(imageProperties, this->m_photoStorageFile);
ImageEncodingProperties imageProperties = new ImageEncodingProperties(); imageProperties.Subtype = "JPEG"; imageProperties.Width = 320; imageProperties.Height = 240; await mediaCaptureMgr.CapturePhotoToStorageFileAsync(imageProperties, photoStorageFile);
var photoProperties = new Windows.Media.MediaProperties.ImageEncodingProperties(); photoProperties.subtype = "JPEG"; photoProperties.width = 320; photoProperties.height = 240; mediaCaptureMgr.capturePhotoToStorageFileAsync(photoProperties, photoStorage).then(…
Приведенные выше фрагменты кода взяты из записи MSDN Sample Media с использованием веб-камеры . Эти 5 строк кода включают веб-камеру, захватывают изображение, увеличивают его до 320 x 240 и сохраняют как файл в формате JPEG. (Да, вы действительно можете использовать веб-камеру и сохранять локальные файлы с помощью JavaScript — при условии, конечно, что пользователь дал разрешение вашему приложению.)
Обратите внимание, что в 5 строках кода используются одинаковые имена объектов и имен свойств, независимо от того, используете ли вы C ++, C # или JavaScript. И обратите внимание, что регистр имен изменится с Title Case для C ++ и C # на Camel Case для JavaScript. Эта способность WinRT «превращаться» в выбранный вами язык называется Projection, и это довольно удивительно. WinRT написан на C ++ как можно быстрее, но его объекты и возможности могут использоваться на всех поддерживаемых языках так же, как если бы WinRT был написан изначально на этом языке.
Как видно из рисунка выше, площадь поверхности WinRT огромна. Все четыре языка снабжены унифицированными API для хранения, захвата мультимедиа, датчиков, GPS и многого другого. Благодаря огромному размеру WinRT и прозрачности его проекции, неудивительно, что разработчики принимают его за «Новый .NET».
Так что .NET Framework мертв? Не за что. На самом деле он все еще жив и здоров с версией 4.5 в режиме рабочего стола. И хотя WinRT предоставляет большую часть того, что нужно разработчикам на C # для создания приложений в стиле Metro, хорошая часть .NET Framework доступна и в режиме Metro. Это может размыть границы между тем, что такое .NET и что такое WinRT, но хорошее практическое правило заключается в том, что компоненты WinRT происходят из пространств имен Windows. *, А компоненты .NET — из System. * .
Для получения более подробной информации об использовании .NET Framework в приложениях в стиле Metro см. Обзор приложений в стиле .NET для Metro .
Компоненты WinRT (и WinMD)
Теперь, когда вы знаете, что такое WinRT и как работает проекция, вы можете подумать: «Не было бы здорово, если бы я мог создавать свои собственные библиотеки WinRT и продавать их или делиться ими с другими разработчиками?» Ну, вы можете, и они могут быть написаны с использованием C ++, C # или Visual Basic. Хотя вы не можете создавать компоненты WinRT с использованием JavaScript сегодня, компоненты, созданные на любом из других языков, автоматически проецируются и могут использоваться всеми языками, включая JavaScript.
Создание компонента WinRT очень похоже на создание библиотеки классов. Фактически проекты библиотек классов можно преобразовать в библиотеки WinRT, просто изменив вывод проекта с «Библиотеки классов» на «Файл WinMD» (MD обозначает MetaData). Существуют правила, которые необходимо соблюдать при создании компонентов WinRT из-за автоматической проекционной системы. Например, открытые методы и свойства должны возвращать только собственные типы WinRT или базовые типы .NET, такие как string и int. Частныйоднако методы и переменные могут быть любого типа .NET, поддерживаемого в программах в стиле Metro. Есть и другие правила, о которых нужно знать, например, как сопоставляются коллекции и как реализовывать асинхронные методы. К счастью, Visual Studio обеспечивает соблюдение этих правил во время компиляции и даже предлагает инструкции о том, как исправить ошибки, если они были допущены.
Вы можете прочитать все правила и требования в статье Создание компонентов среды выполнения Windows в C # и Visual Basic . Имейте в виду, что библиотеки классов .NET по-прежнему поддерживаются, поэтому, если вы не хотите взаимодействовать с C ++ или JavaScript, вам может вообще не понадобиться создавать компонент WinRT. Если вам нужен WinRT, обязательно посмотрите Создание простого компонента в C # или Visual Basic и вызов его из JavaScript .
Async и Await
Возможно, вы заметили новое ключевое слово await перед вызовом CapturePhotoToStorageFileAsync в приведенном выше примере. Когда вы слышите, как команда Windows говорит «Fast and Fluid», они имеют в виду это. И стоять за этим утверждением они сделали каждый вызов WinRT , которые могли бы , возможно , занимает больше времени , чем 50 миллисекунд асинхронными.
Но асинхронное программирование сложно, правда? Я имею в виду, что можно просто выполнить поиск «Silverlight asynchronous» и найти длинный список разработчиков, которые были расстроены, приходя из мира настольных компьютеров, чтобы выяснить, что они больше не могут блокировать звонки онлайн-сервисам. Но разработчики не расстроились из-за того, что не смогли заблокировать поток пользовательского интерфейса, они были расстроены тем, насколько уродливым выглядит их код и насколько сложнее его поддерживать. Чтобы проиллюстрировать эту проблему, рассмотрите следующий код Silverlight для загрузки страницы с URL-адреса:
private void DownloadPage() { WebClient client = new WebClient(); client.DownloadStringCompleted += DownloadStringCompleted; client.DownloadStringAsync(new Uri("http://www.bing.com")); } private void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Only proceed if there wasn't an error if (e.Error == null) { … } }
В этом примере управлении потоком фактически уходит в DownloadPage метод и переход к совершенно новому методу , называемого DownloadStringCompleted . Любые данные (или состояние), которые существовали в методе DownloadPage, недоступны в DownloadStringCompleted . Если необходимо сделать выбор в обратном вызове на основе переменных, определенных в DownloadPage , они должны быть преобразованы в переменные уровня класса или каким-либо образом переданы обработчику.
Анонимные методы и лямбды очень помогают в этом. Например, мы могли бы переписать наш пример выше, чтобы он выглядел так:
private void DownloadPage() { WebClient client = new WebClient(); client.DownloadStringCompleted += (o, e) => { // Only proceed if there wasn't an error if (e.Error == null) { … } }; client.DownloadStringAsync(new Uri("http://www.bing.com")); }
Теперь обработчик на самом деле внутри в DownloadPage метод и имеет доступ ко всем своим локальным переменным. (Кстати, это работает из-за менее известной функции современных языков программирования, называемой замыканиями .)
Проблема этого подхода состоит в том, что он приводит к тому, что я люблю называть «смертью по абзацу». Например, если обработчик теперь должен вызывать что-то еще как асинхронный, требуется другая лямбда.
private void DownloadPage() { WebClient client = new WebClient(); client.DownloadStringCompleted += (o, e) => { // Only proceed if there wasn't an error if (e.Error == null) { WebClient client2 = new WebClient(); client2.DownloadStringCompleted += (o, e) => { // Only proceed if there wasn't an error if (e.Error == null) { … } }; client2.DownloadStringAsync(new Uri("http://www.microsoft.com")); } }; client.DownloadStringAsync(new Uri("http://www.bing.com")); }
И если этот обработчик должен вызвать другой асинхронный метод, я думаю, вы видите проблему. Код грязный и не выглядит естественным. Мы даже пишем, что мы хотим сделать с результатами, прежде чем писать строки кода, которые их выбирают! Он чувствует себя «с ног на голову». Вся эта работа необходима, потому что мы просим разработчиков компенсировать тот факт, что асинхронные методы завершают использование обратных вызовов.
Но что, если бы мы могли написать код, который выглядит как блокирующий, но на самом деле дает время, пока асинхронный вызов завершается? Вот что делает ключевое слово await , и с его помощью наш пример можно записать в Windows 8 следующим образом:
private async void DownloadPage() { HttpClient client = new HttpClient(); string bing = await client.GetStringAsync("http://www.bing.com"); string ms = await client.GetStringAsync("http://www.microsoft.com"); }
Какой из них вы бы предпочли сохранить?
Чтобы узнать больше о том, почему асинхронное программирование так важно в Windows 8, смотрите раздел Быстрое и плавное выполнение приложений с помощью асинхронности в среде выполнения Windows . Чтобы глубже погрузиться в работу асинхронного ключевого слова и изучить сложные темы, такие как получение отчетов о ходе выполнения, ознакомьтесь с великолепной публикацией Diving deep with WinRT и ждите .
В следующей статье я расскажу о различиях в API между Silverlight для Windows Phone и приложениями в стиле Windows Runtime для Metro.