Windows Phone 7.1 SDK открыл ряд новых возможностей в разработке, в том числе доступ к камерам в устройствах под управлением Windows Phone 7.5. После выпуска Windows Phone 8 появились еще более интересные новые возможности, например захват видео высокой четкости с разрешением 1080p по поддерживаемом аппаратном обеспечении. Хотя Windows Phone 7.1 SDK можно было бы расширить для поддержки новых функций устройств, выпуск Windows Phone 8 также совпал с дополнительными архитектурными изменениями в ОС: у нее общее с Windows 8 ядро. Еще одно крупное изменение заключалось в переносе ряда API на модель Windows 8 и использовании новой исполняющей среды, известной как Windows Runtime (WinRT).
Windows Phone 7.1 SDK — это версия, позволяющая разработчикам ориентироваться на функциональность Windows Phone 7.5. С этого момента я буду ссылаться на Windows Phone 7.5, подразумевая как версию ОС, так и SDK. (Кстати, кодовым названием Windows Phone 7.5 было «Mango», а кодовым названием Windows Phone 8 — «Apollo»; вы наверняка встречали эти названия в различной документации.)
Windows Runtime не только вводит ряд API, общих для Windows Phone и Windows 8, но и образует гораздо более эффективную исполняющую среду, позволяющую предоставлять новые API, специфичные для Windows Phone, на основе единой модели. Я не стану углубляться в новшества, привнесенные Windows Runtime, но вы должны понимать, что это повлияло на API, в том числе те, которые обеспечивают захват видео.
Я дам обзор того, что изменилось между версиями, но основной замысел в том, чтобы вы научились поддерживать свои проекты для Windows Phone 7.5 и параллельно предоставлять более богатую функциональную среду пользователям Windows Phone 8. Здесь огромное преимущество заключается в том, что обсуждаемые методики применимы не только к захвату видео, но и к любым API, которые были переделаны под Windows Phone 8. Я буду использовать имеющиеся в открытом доступе образцы кода, чтобы нагляднее объяснить методики, позволяющие повторно использовать код между проектами для Windows Phone 7.5 и 8 и даже проектами для Windows 8.
Приступаем
Одна из основных задач, которые я хочу проиллюстрировать, — как перенести решение в Windows Phone 8, не пренебрегая устройствами с Windows Phone 7.5. Прежде чем заняться этим, стоит сделать шаг назад и отметить, что во многих случаях вам не придется делать вообще ничего. Ваше существующее приложение Windows Phone 7.5 будет по-прежнему выполняться в Windows Phone 8, хотя вы должны протестировать его, чтобы быть уверенным в отсутствии какого-либо неожиданного поведения. Visual Studio 2012 позволит вам при ориентации на Windows Phone 7.5 использовать только поддерживаемые API. И помните, что вам нужно беспокоиться лишь о написании кода для Windows Phone 8, если вы хотите задействовать преимущества любого из новых API и предлагаемую ими функциональность.
Я буду исходить из того, что вы захотите продолжить поддержку своих пользователей в Windows Phone 7.5, в то же время открывая пользователям Windows Phone 8 доступ к новым возможностям их устройств.
Чтобы вам было проще понять, как можно создать наилучшее решение, я использую в качестве отправной точки пример Windows Phone (7.5) Video Recorder Sample (bit.ly/16tM2c1). Помните: хотя я использую здесь пример кода для записи видео, обсуждаемые мной подходы применимы к любым средствам, которые вы хотите задействовать на разных версиях платформы.
В этом решении следует обратить внимание на несколько моментов.
- В нем не используется шаблон Model-View-ViewModel (MVVM):
- большая часть UI содержится в файле MainPage.xaml;
- большая часть логики находится в файле отделенного кода MainPage.xaml.cs.
- Для операций с камерой применяется Windows Phone 7.5 API, а именно класс System.Windows.Media.VideoCaptureDevice. В Windows Phone 8 в этой области введен новый API, о котором я расскажу позднее.
Тот факт, что в этом решении не применяется шаблон MVVM, не особо важен — он помог бы вам сэкономить время на последующем рефакторинге (и обычно рекомендуется к использованию в производственном коде), но его наличие или отсутствие не создает проблемы совместимости.
Однако класс VideoCaptureDevice ограничит вас при переходе на Windows Phone 8. Он будет нормально работать в исполняющей среде Windows Phone 8, но не обеспечит максимальной производительности и не позволит использовать весь диапазон разрешений, поддерживаемых аппаратным обеспечение (не считая других мелочей).
В следующих разделах я буду придерживаться такого подхода:
- создание общей абстракции для функциональности, которая будет по-разному реализоваться на разных платформах. Это позволит вам разделять код, использующий такую функциональность;
- абстракция видеорекордера помещается в Portable Class Library (PCL). Это упростит потом перенос другого кода в PCL, если понадобится;
- общее использование отделенного кода (codebehind) для MainPage через связывание файла (прагматичное решение, так как существующее приложение не использует шаблон MVVM; в ином случае я посоветовал бы перенести модели представлений в PCL).
Итак, учитывая, что я хочу реализовать лучший подход, мне нужно подумать о том, что можно абстрагировать в логике управления камерой в Windows Phone 7.5 из файла MainPage.xaml.cs. Эту задачу мы будем решать в следующем разделе.
Разделение и абстрагирование вашей реализации
Первым делом вы должны определиться, куда вы будете помещать свой код. Библиотека классов имела бы смысл, но вы можете использовать PCL. PCL позволяет указывать целевые платформы и автоматически ограничивает вас теми API, которые имеются на каждой из выбранных вами платформ. В конечном счете это дает возможность создавать двоичные ссылки между проектами (вместо простого связывания кода/проекта и перекомпиляции под каждую целевую платформу). В данном случае можно создать проект PCL, ориентированный на Windows Phone 7.5 или более поздние версии и приложения Windows Store (т. е. на так называемые «современные приложения» для Windows 8), и назвать его VideoRecorder.Shared (рис. 1).
Рис. 1. Настройка вашей библиотеки PCL
Следуя шаблону абстракции (bit.ly/YQwsVD), можно создать абстрактный класс VideoRecorderCore, который позволит вам при необходимости писать код, специфичный для платформы. Этот абстрактный класс выглядит примерно так, как показано на рис. 2.
Рис. 2. Абстрактный класс VideoRecorderCore
namespace VideoRecorder.Shared
{
public abstract class VideoRecorderCore
{
public abstract void InitializeVideoRecorder();
public abstract void StartVideoRecording();
public abstract void StopVideoRecording();
public abstract void StartVideoPreview();
public abstract void DisposeVideoPlayer();
public abstract void DisposeVideoRecorder();
public static VideoRecorderCore Instance { get; set; }
}
}
Примечание В примере на рис. 2 вы могли бы легко использовать какой-нибудь интерфейс, но скорее всего во многих сценариях вам потребуется некая общая базовая функциональность.
В проекте sdkVideoRecorderCS вы могли бы идеально провести какое-то время, реализуя шаблон MVVM, но оставим это на другой раз. На данном этапе рефакторинг заключается в основном в том, чтобы предоставить конкретную реализацию абстрактного класса. К счастью, у вас уже есть конкретная реализация — просто сейчас она слишком жестко связана в MainPage.xaml.cs. Для решения этой проблемы можно создать класс WP7VideoRecorder в проекте sdkVideoRecorderCS и наследовать его от VideoRecorderCore в проекте PCL. Далее вам понадобится лишь выделить реализацию из MainPage.xaml.cs и поместить в подходящий переопределенный метод. В качестве примера метод InitializeVideoRecorder изначально мог бы выглядеть, как на рис. 3.
Рис. 3. Метод InitializeVideoRecorder
public override void InitializeVideoRecorder()
{
if (captureSource == null)
{
captureSource = new CaptureSource();
fileSink = new FileSink();
videoCaptureDevice = CaptureDeviceConfiguration.
GetDefaultVideoCaptureDevice();
captureSource.CaptureFailed += new
EventHandler<ExceptionRoutedEventArgs>(OnCaptureFailed);
if (videoCaptureDevice != null)
{
videoRecorderBrush = new VideoBrush();
videoRecorderBrush.SetSource(captureSource);
viewfinderRectangle.Fill = videoRecorderBrush;
captureSource.Start();
UpdateUI(ButtonState.Initialized,
"Tap record to start recording...");
}
else
{
UpdateUI(ButtonState.CameraNotSupported,
"A camera is not supported on this device.");
}
}
}
Я не буду обсуждать здесь каждую строку на рис. 3 — этот код исчерпывающе описан в документации (bit.ly/YVIf0I), но, если в двух словах, то он инициализирует экземпляр VideoCaptureDevice, затем настраивает предварительный просмотр видео в UI. Я внес сюда парочку проблем из-за простого копирования и вставки из отделенного кода в конкретную реализацию. Этот код ссылается на UI-элементы и методы (например, viewfinderRectangle и UpdateUI). Вам не нужно этого в вашей конкретной реализации, и, если бы вы уже ввели модель представления, от них можно было бы легче избавиться. Так что здесь потребуется дополнительная работа по чистке кода.
- Переместите UI-код обратно в соответствующий UI-метод (в данном случае в InitializeVideoRecorder из файла MainPage.xaml.cs).
- Создайте новый абстрактный метод для инициализации VideoRecorderBrush, так как это потребуется и в Windows Phone 7.5, и в Windows Phone 8, но реализации могут отличаться.
После этого ваш метод будет выглядеть примерно так:
public override void InitializeVideoRecorder()
{
if (_captureSource == null)
{
_captureSource = new CaptureSource();
_fileSink = new FileSink();
_videoCaptureDevice = CaptureDeviceConfiguration.
GetDefaultVideoCaptureDevice();
_captureSource.CaptureFailed +=new
EventHandler<ExceptionRoutedEventArgs>(OnCaptureFailed);
}
}
Теперь вы можете использовать преимущества отделенного кода Windows Phone (MainPage.xaml.cs), который работает как в Windows Phone 7.5, так и в Windows Phone 8 (рис. 4).
Рис. 4. Отделенный код, работающий в Windows Phone 7.5 и 8
public void InitializeVideoRecorder()
{
_videoRecorder.InitializeVideoRecorder();
_videoRecorder.CaptureFailed += OnCaptureFailed;
if (_videoRecorder.VideoCaptureDevice != null)
{
videoRecorderBrush = new VideoBrush();
videoRecorderBrush.SetSource(
_videoRecorder.VideoSource as CaptureSource);
viewfinderRectangle.Fill = videoRecorderBrush;
_videoRecorder.StartVideoSource();
UpdateUI(ButtonState.Initialized,
"Tap record to start recording...");
}
else
{
UpdateUI(ButtonState.CameraNotSupported,
"A camera is not supported on this device.");
}
}
Вам нужно лишь инициализировать экземпляр VideoRecorder, и вы готовы к использованию новой реализации, специфичной для платформы:
_videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();
Если не считать нескольких мелких изменений, вы можете просто выполнить аналогичный рефакторинг остальных методов.
Ориентация на новые API в Windows Phone 8
Как уже говорилось, перенос вашего кода для Windows Phone 7.5 в Windows Phone 8 довольно тривиален: вы должны лишь открыть проект и выбрать Upgrade to Windows Phone 8.0 (рис. 5). Но как быть, если вам нужно поддерживать обе версии? Следуя той же схеме, код теперь структурируется так, чтобы было легко поддерживать повторное использование. Поскольку вы выделили видеорекордер, вы можете задействовать новый, более мощный WinRT API для захвата видео с камеры в Windows Phone 8.
Рис. 5. Обновление проекта для ориентации на исполняющую среду Windows Phone 8
Следующий шаг — создать проект Windows Phone 8, повторно используя как можно больше кода и вводя необходимую вам новую функциональность.
К этому моменту у вас появляется два варианта при создании версии для Windows Phone 8. Вы можете либо скопировать существующий проект и обновить его, либо создать новый проект Windows Phone 8 с нуля и добавить дубликаты файлов кода и ссылок. На самом деле решение сводится к тому, насколько крупный ваш проект и сколько кода вы хотели бы использовать повторно. В этом примере мы сделаем копию проекта и обновим его, т. е. мы будем отбирать наиболее пригодные части для повторного использования, и это уменьшит вероятность потери каких-либо ключевых файлов. Вот что вам понадобится сделать.
- Скопировать проект sdkVideoRecorderCS и переименовать sdkVideoRecorderCS.csproj в sdkVideoRecorderCS.WP8.csproj. Затем добавить новый проект в существующее решение, чтобы было проще сопровождать обе версии.
- Обновите новый проект до Windows Phone 8. Вы обнаружите, что обновленный проект нормально выполняется в Windows Phone 8 безо всяких дополнительных изменений — благодаря обратной совместимости API. Преимущества этого в том, что не нужны никакие изменения в коде, но расплатой за это может оказаться меньшая производительность, так как вы не сможете задействовать новейшие и важнейшие средства платформы.
- Теперь можно отобрать элементы, которые вы хотели бы использовать повторно. Например, MainPage.xaml и MainPage.xaml.cs по большей части останутся одинаковыми между разными версиями (по крайней мере, в этом решении), поэтому удалите существующую версию этих файлов в проекте Windows Phone 8 и добавьте файловую ссылку на версии в проекте Windows Phone 7.5, щелкнув его правой кнопкой мыши, выбрав Add Existing Item и указав файл MainPage.xaml, задав Add As Link (рис. 6).
Рис. 6. Добавление файловой ссылки
Примечание Вместо прохода по только что описанным меню можно просто перетащить файл MainPage.xaml из одного проекта в другой (целевой), удерживая клавиши Ctrl+Shift. Это приведет к созданию файловой ссылки и автоматическому перемещению файла отделенного кода.
Эти операции нужно будет повторить для всех релевантных файлов, но для целей данной демонстрации это единственная файловая ссылка, которую вам понадобится добавить и повторно использовать.
В качестве отступления замечу, что зачастую некоторые слишком утопично подходят к повторному использованию кода. На практике бывает так, что вы хотите использовать один и тот же код на разных платформах, но они могут требовать всего пары мелких отличий. В таких ситуациях было бы неразумно поддерживать два набора кода и абстрагировать специфику каждой платформы. Здесь зачастую лучше использовать директивы препроцессора для ориентации отдельных строк кода на конкретную платформу. Соответствующий пример я покажу позднее, но важно не увлекаться чрезмерно этой методикой, чтобы не превратить свой код в полную кашу.
Хороший пример того, когда следует использовать абстракцию платформы, — у вас имеется изолированная часть функциональности, специфичная для платформы. В текущем примере это логика видеорекордера. Сейчас в вашем проекте Windows Phone 8 содержится файл WP7VideoRecorder.cs, который работает нормально, но не использует новый Windows Phone 8 API, и как раз это мы собираемся изменить. Удалите файл WP7VideoRecorder.cs из проекта Windows Phone 8 и создайте новый файл WP8VideoRecorder.cs, который тоже наследует от VideoRecorderCore.
Как и раньше, реализуйте каждый из методов. На этот раз основное отличие в том, что вы будете использовать пространство имен Windows.Phone.Media.Capture, а не System.Windows.Media. Это более новое пространство имен WinRT, в котором присутствует класс AudioVideoCaptureDevice. Он служит аналогичным целям, что и ранее применявшийся класс VideoCaptureDevice, но имеет гораздо более богатую функциональность.
В отношении нескольких подходов WinRT API ведут себя иначе, чем их предшественники. Одно из таких изменений, с которыми вы столкнетесь здесь, заключается в том, что многие API являются асинхронными (подробности позже). Другое отличие в том, что вы имеете дело с хранилищем, использующим пространство имен Windows.Storage вместо привычного System.IO.IsolatedFileStream (хотя оно по-прежнему применяется для поддержки воспроизведения медиа-информации).
Теперь я кратко обрисую несколько более крупных изменений, чтобы показать некоторые отличия в сценарии с видеорекордером и более универсальный подход к разрешению отличий этих типов, в то же время сохраняя элемент повторного использования кода.
В версии кода видеозаписи для Windows Phone 7.5 было определено несколько закрытых переменных:
private CaptureSource _captureSource;
private VideoCaptureDevice _videoCaptureDevice;
private IsolatedStorageFileStream _isoVideoFile;
private FileSink _fileSink;
private string _isoVideoFileName = "CameraMovie.mp4";
В идеале, вы предпочтете максимально сблизить две кодовые базы, но в новом API нет нужды в CaptureSource или FileSink: CaptureSource заменяется реальным экземпляром VideoCaptureDevice, который может действовать как источник, а функциональность FileSink вы получаете автоматически, поэтому вам нужен лишь StorageFile для его сохранения:
private AudioVideoCaptureDevice _videoCaptureDevice;
private IsolatedStorageFileStream _isoVideoFile;
private StorageFile sfVideoFile;
private string _isoVideoFileName = "CameraMovie.mp4";
Следующие логичные шаги — проработка конкретных реализаций методов. Я снова начну с InitializeVideoRecorder. Вы уже видели, как выглядел этот метод раньше, и здесь вам нужно просто аналогичном образом инициализировать экземпляр AudioVideoCaptureDevice:
public async override void InitializeVideoRecorder()
{
CameraSensorLocation location = CameraSensorLocation.Back;
var captureResolutions =
AudioVideoCaptureDevice.
GetAvailableCaptureResolutions(location);
_videoCaptureDevice = await AudioVideoCaptureDevice.
OpenAsync(location, captureResolutions[0]);
_videoCaptureDevice.RecordingFailed += OnCaptureFailed;
}
Как видите, синтаксис отличается, но код служит той же цели. Эта часть кода также позволяет настраивать дополнительные свойства камеры, например указывать разрешение захватываемого видео и какую камеру (если доступно более одной камеры) вы хотели бы задействовать. Получаемый вами контроль над разрешениями уже является преимуществом по сравнению с тем, что вы получали в Windows Phone 7.5 API, так как это дает возможность записывать полноформатное изображение высокой четкости с разрешением 1080p, если камера в вашем устройстве поддерживает его. Как и в случае многих WinRT API, еще одно положительное отличие заключается в том, что методы являются асинхронными. Для упрощения сопровождения можно использовать ключевые слова async и await в C# 5. Я не стану вдаваться в детали этих средств, поскольку они уже подробно рассматривались в ряде других статей, но они помогут как получить преимущества асинхронного кода, так и уменьшить сложность работы с таким кодом.
Теперь рассмотрим метод InitializeFileSink. Хотя вам больше не нужен FileSink как таковой, вы все же должны инициализировать файлы, в которых вы сохраняете захваченное видео. И здесь вы начинаете работать с пространством имен Windows.Storage, содержащим такие классы, как StorageFolder и StorageFile:
public async override void InitializeFileSink()
{
StorageFolder isoStore = ApplicationData.Current.LocalFolder;
sfVideoFile = await isoStore.CreateFileAsync(
_isoVideoFileName,
CreationCollisionOption.ReplaceExisting);
}
Нет нужды обсуждать каждый метод, потому что о важнейших отличиях уже было сказано. Методы, которые остаются теми же в обеих версиях, можно переместить в базовый класс VideoRecorderCore, чтобы обеспечить общее поведение подклассов.
Внеся оставшиеся изменения, вы заметите, что при компиляции следующей строки возникает ошибка:
_videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();
Причина весьма очевидна — она связана с тем, что код для Windows Phone 8 ссылается на класс, который больше не существует: WP7VideoRecorder. Конечно, вы должны ссылаться на новый класс WP8VideoRecorder, но внесение этого изменения в файл MainPage.xaml.cs разрушило бы код для Windows Phone 7.5, так как вы связываете один и тот же файл.
У вас несколько вариантов. Вы могли бы использовать такую директиву препроцессора:
#if WP7
_videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();
#else
_videoRecorder = VideoRecorderCore.Instance = new WP8VideoRecorder();
#endif
Если бы вы выбрали вариант с директивами препроцессора, то более аккуратным подходом было бы их перемещение в класс-оболочку, например введите метод CreateVideoRecorder в абстрактный базовый класс VideoRecorderCore и поместите в него предыдущее объявление #if.
Я решил избегать директив препроцессора и абстрагировать процесс поиска конкретного класса в отдельный класс фабрики — это означает, что код, использующий рекордер, становится согласованным между Windows Phone 7.5 и 8:
_videoRecorder =
VideoRecorderCore.Instance = VideoRecorderFactory.CreateVideoRecorder();
Альтернативный подход — использовать встраивание зависимости (Dependency Injection, DI) или шаблон Service Locator и возложить ответственность за разрешение экземпляра соответственно на контейнер или сервис.
Менее значимая проблема — настройка VideoBrush для предварительного просмотра с камеры. В этом случае вы вновь можете подумать о методике с директивами препроцессора #if для учета мелких различий в деталях реализации:
#if WP7
videoRecorderBrush.SetSource(_videoRecorder.VideoSource as CaptureSource);
#else
videoRecorderBrush.SetSource(_videoRecorder.VideoSource);
#endif
Это нужно потому, что в Windows Phone 8 метод SetSource перегружается, чтобы в качестве объекта Source можно было указать какой-либо объект. Вот почему вы можете возвращать экземпляр AudioVideoCaptureDevice в свойстве VideoSource для Windows Phone 8. Поддержка этого подхода требует добавить новый символ условной компиляции «WP7» в проект Windows Phone 7.5 через Project Properties | Build (рис. 7).
Windows Runtime интенсивно использует асинхронные методы, чтобы повысить отзывчивость приложения.
Рис. 7. Добавление собственного символа условной компиляции в проект для Windows Phone 7.5
Как уже упоминалось, Windows Runtime интенсивно использует асинхронные методы, чтобы повысить отзывчивость приложения, но, если вы разделяете код между разными версиями платформы, как это повлияет на реализацию? Хотя вы можете писать код на C# 5 в проекте, ориентированном на Windows Phone 7.5, некоторые детали реализации будут недоступны, когда вы захотите использовать ключевые слова async и await. Рассмотрим следующий метод:
public async override void InitializeVideoRecorder()
{
CameraSensorLocation location = CameraSensorLocation.Back;
var captureResolutions =
AudioVideoCaptureDevice.
GetAvailableCaptureResolutions(location);
_videoCaptureDevice = await AudioVideoCaptureDevice.
OpenAsync(location, captureResolutions[0]);
_videoCaptureDevice.RecordingFailed += OnCaptureFailed;
}
Здесь устанавливается свойство VideoCaptureDevice, но использующий его код выглядит так:
_videoRecorder.InitializeVideoRecorder();
if (_videoRecorder.VideoCaptureDevice != null)
{
...
}
else
{
UpdateUI(ButtonState.CameraNotSupported,
"A camera is not supported on this device.");
}
Не сразу бросается в глаза, что, пока вы ожидаете выполнения вызова метода AudioVideoCaptureDevice.OpenAsync, проверка _videoRecorder.VideoCaptureDevice может уже завершиться. Другими словами, проверка VideoCaptureDevice может дать null, потому что это свойство еще не инициализировано.
И на этом этапе у вас есть несколько вариантов дальнейших действий. Чтобы сделать использующий код асинхронным, вам придется перекроить свой код, а это — в зависимости от конкретного решения — может потребовать массы усилий. Если вы хотите избежать этого, то могли бы просто обернуть асинхронный код в синхронный метод-оболочку. Другая альтернатива — механизм на основе событий, уведомляющий потребителя о завершении инициализации.
Не исключаю, что вы захотите воспользоваться этой возможностью для перехода на новую асинхронную модель и ожидать окончания вызова InitializeVideoRecorder с помощью await, но как сделать это, если Windows Phone 7.5 SDK не поддерживает требуемые конструкции? Одно из решений — скачать NuGet-пакет «Async for .NET Framework 4, Silverlight 4 and 5, and Windows Phone 7.5» в Visual Studio, как показано на рис. 8.
Рис. 8. Найдите асинхронный NuGet-пакет для Windows Phone 7.5 поиском по «microsoft.bcl.async»
На момент написания этой статьи данный пакет все еще был в состоянии предварительной версии, но вскоре ожидается появление стабильного выпуска. Если вы хотите пойти более надежный на данный момент путем, вам придется переработать свой код, как было описано ранее, и ввести шаблон событий или синхронную оболочку.
Примечание Убедитесь, что у вас установлен NuGet Package Manager версии не ниже 2.1, а иначе вы будете получать сообщения о неожиданной ошибке при попытке использовать этот пакет. Последнюю версию NuGet Package Manager можно скачать по ссылке bit.ly/dUeqlu.
После загрузки этого пакета вы можете «выровнять» код в каждом проекте. Например, вы можете ввести поддержку async в свой абстрактный базовый класс, возвращая тип Task вместо void; это позволит вам ожидать результат с применением await, а не использовать текущий механизм «выстрелил и забыл». Кроме того, по соглашению, считается хорошим стилем добавлять «Async» к именам асинхронных методов, что делает использование этих методов интуитивно понятными разработчику. Так, некоторые методы теперь будут иметь сигнатуру вроде этой:
public abstract Task InitializeVideoRecorderAsync();
public abstract Task InitializeFileSinkAsync();
После этого вам, конечно, может потребоваться рефакторинг кода в подклассах, например:
public override async Task InitializeVideoRecorderAsync()
{
И наконец, вы сможете обновить код в MainPage.xaml.cs для поддержки ожидания результатов асинхронных операций. Вот пример:
public async void InitializeVideoRecorder()
{
await _videoRecorder.InitializeVideoRecorderAsync();
Внеся эти последние изменения, вы должны увидеть, что теперь можете успешно выполнять проекты Windows Phone 7.5 и Windows Phone 8. Вы получаете довольно высокую степень повторного использования кода и можете применять новейшие средства там, где они поддерживаются. Это и будет лучшим решением!
Как насчет Windows 8?
Многие партнеры, с которыми я работаю, создавали приложения для Windows Phone с применением XAML и C#. Когда они рассматривают возможность разработки приложения Windows Store для Windows 8, их первый технический вопрос: по какому пути им следует двигаться — HTML5/JavaScript или XAML/C#? Решение для Windows Phone с тщательно продуманной архитектурой может предложить большой объем кода, который будет повторно использоваться в Windows 8, поэтому данный фактор может оказаться решающим при выборе языка, на котором вы будете разрабатывать свое приложение Windows Store.
Хотя вы сможете повторно использовать код между Windows Phone 7.5 и Windows 8, есть ряд сценариев, где понадобится ветвление кодовых баз для этих ОС. Один из таких сценариев — видеорекордер, но хорошая новость в том, что рассмотренные ранее подходы позволят вам сэкономить массу времени и сил.
Реализацию версии этого кода для Windows 8 я оставляю в качестве упражнения для читателей; впрочем, вы можете скачать пакет исходного кода для этой статьи, где, в том числе, есть и решение для Windows 8.
Примечание Для реализации под Windows 8 вы должнгы использовать класс Windows.Media.Capture.MediaCapture.
Шаблон MVVM
То, что вы недавно реализовали, дает вам 100% повторное использование кода MainPage.xaml для двух версий Windows Phone. Если вы прошли этапы абстрагирования и рефакторинга, то сможете добиться почти 100% повторного использования и сопутствующего файла отделенного кода. Вы могли бы еще больше расширить эти методики за счет шаблона MVVM, и это помогло бы обеспечить высокую степень повторного использования не только между версиями Windows Phone, но и в кодовой базе для Windows 8.
Эти методики позволяют добиться повторного использования кода при абстрагировании специфичных для платформы средств — не только для захвата видео, но и для управления жизненным циклом приложения (application lifecycle management, ALM), хранилища файлов и многого другого.