За последнее десятилетие Bluetooth стал широко распространенной технологией для беспроводной связи малой дальности между устройствами, такими как мобильные телефоны, персональные компьютеры и гарнитуры. Bluetooth Special Interest Group (BTSIG) — это отраслевой орган, определяющий стандарты беспроводных сервисов в спецификациях Bluetooth Profile.
Один из таких профилей — Object Push Profile (OPP), который применяется для передачи файлов с одного устройства на другое. Object Exchange Protocol (OBEX) является частью фундамента для OPP. OBEX также используется в профилях, отличных от OPP, поскольку это универсальный протокол для передачи объектов между устройствами.
Для разработчиков, желающих задействовать OBEX в своих приложениях, я создал набор API поверх Windows Runtime (WinRT) Bluetooth API, предоставляющих OBEX в приложении. Эти API поставляются как библиотечный пакет для универсальных приложений, а значит, приложения Windows Store и Windows Phone Silverlight могут использовать тот же набор API.
OBEX и OPP
Сначала важно понять, что такое OBEX и OPP и как они работают. OPP позволяет одному Bluetooth-устройству отправлять файл или объект другому Bluetooth-устройству с поддержкой OPP. Изначально применение OBEX задумывалось для обмена файлами по каналам инфракрасной (ИК) связи. BTSIG решил повторно использовать этот протокол для обмена файлами по Bluetooth. За исключением нижележащей транспортной среды OBEX поверх ИК-канала и OBEX поверх Bluetooth аналогичны.
OBEX основан на клиент-серверной модели, в которой принимающее Bluetooth-устройство выполняет OBEX-сервер, который прослушивает и принимает клиентские соединения. Клиентское Bluetooth-устройство подключается к серверному Bluetooth-устройству, образуя потоковый канал поверх Bluetooth. Требования к аутентификации до подключения и передачи объекта зависят от сервиса или приложения, использующего OBEX. Например, OPP может разрешать не аутентифицированное соединение, чтобы упростить процесс быстрого обмена валют. Другие сервисы, использующие OBEX, могут разрешать только аутентифицированные соединения.
Обмен файлами осуществляется с помощью пакетов трех типов. Эти пакеты известны как первый PUT, промежуточный PUT и последний PUT. Пакет «первый PUT» отмечает инициализацию передачи файла, а «последний PUT» — ее окончание. Множество пакетов «промежуточный PUT» содержат собственно данные. После приема каждого PUT-пакета сервер возвращает клиенту пакет подтверждения.
В типичном сценарии OBEX-пакеты посылаются следующим образом.
- OBEX-клиент подключается к устройству-серверу, посылая пакет запроса на соединение. Этот пакет указывает максимальный размер пакетов, которые может принимать клиент.
- Получив ответ от сервера, указывающий на принятие соединения, OBEX-клиент посылает пакет «первый PUT». Он содержит метаданные, описывающие объект, в том числе имя файла и размер. (Хотя протокол OBEX также допускает включение в пакет «первый PUT» данных объекта, реализация OBEX в созданной мной библиотеке не отправляет никаких данных в пакете «первый PUT».)
- После приема подтверждения о том, что сервер получил пакет «первый PUT», OBEX-клиент передает несколько PUT-пакетов, содержащих данные объекта. Длина этих пакетов ограничивается максимальным размером пакетов, поддерживаемом сервером, и этот размер выставляется в ответе сервера на пакет запроса на соединение в п. 1.
- Пакет «последний PUT» содержит константу и заключительную порцию данных объекта.
- По окончании передачи файла OBEX-клиент передает пакет запроса на отключение и закрывает Bluetooth-соединение. Протокол OBEX разрешает повторение операций в пп. 2–3 для отправки нескольких объектов по одному соединению.
В любой момент OBEX-клиент может отменить процесс передачи данных, отправив пакет ABORT. Передача данных прекращается немедленно. В написанной мной библиотеке детали реализации протокола OBEX скрыты, и вы увидите только высокоуровневые API.
OBEX-библиотека
Клиентская библиотека Bluetooth OBEX для приложений Windows Store разработана как портируемая и может работать с приложениями Windows Store и Windows Phone Silverlight 8.1. Она содержит три DLL, которые превращают библиотеку в исполняющую среду для OBEX-клиента. Каждая DLL предназначена для обработки конкретной задачи: Bluetooth.Core.Service, Bluetooth.Core.Sockets и Bluetooth.Services.Obex.
Bluetooth Core Service Файл Bluetooth.Core.Service.dll содержит пространство имен Bluetooth.Core.Service. Эта библиотека обеспечивает поиск и подсчет находящихся рядом Bluetooth-устройств в паре с клиентским устройством (табл. 1). В настоящее время она ограничена разовым определением количества сопряженных устройств. В будущих версиях появится средство наблюдения для постоянного поиска дополнительных Bluetooth-устройств.
Табл. 1. Методы и соответствующие события из BluetoothService
Метод (с параметрами) | Сопоставленные события |
[static] GetDefault | Никаких событий не сопоставлено |
SearchForPairedDevicesAsync | Успех: SearchForDevicesSucceeded Неудача: SearchForPairedDevicesFailed |
Базовый сервис Bluetooth представлен статическим классом BluetoothService (рис. 1). В этом классе есть API для асинхронного подсчета устройств.
Рис. 1. BluetoothService, подсчитывающий сопряженные устройства
BluetoothService btService = BluetoothService.GetDefault();
btService.SearchForPairedDevicesFailed +=
btService_SearchForPairedDevicesFailed;
btService.SearchForPairedDevicesSucceeded +=
btService_SearchForPairedDevicesSucceeded;
await btService.SearchForPairedDevicesAsync();
void btService_SearchForPairedDevicesSucceeded(object sender,
SearchForPairedDevicesSucceededEventArgs e)
{
// Получаем список сопряженных устройств
// из набора e.PairedDevices
}
void btService_SearchForPairedDevicesFailed(object sender,
SearchForPairedDevicesFailedEventArgs e)
{
// Получаем причину неудачи из перечисления e.FailureReason
}
Bluetooth Core Sockets Файл Bluetooth.Core.Sockets.dll содержит пространство имен Bluetooth.Core.Sockets и предназначен для поддержки потоковых операций через сокет по Bluetooth-соединению. Функциональность сокетов предоставляется через класс BluetoothSockets (табл. 2). Этот сокет не относится ни к TCP, ни к UDP. Все взаимодействие с принимающим устройством (recipient device) происходит через BluetoothSockets.
Табл. 2. Методы и соответствующие события из BluetoothSockets
Метод (с параметрами) | Сопоставленные события |
Constructor(Bluetooth.Core.Services.BluetoothDevice) Constructor(Bluetooth.Core.Services.BluetoothDevice, System.UInt32) Constructor(Bluetooth.Core.Services.BluetoothDevice, System.String) Constructor(Bluetooth.Core.Services.BluetoothDevice, System.UInt32, System.String) | Никаких событий не сопоставлено |
PrepareSocketAsync | Успех: SocketConnected Неудача: ErrorOccured |
SendDataAsync(System.Byte[]) SendDataAsync(System.String) | Никаких событий не сопоставлено |
CloseSocket | SocketClosed |
Никаких методов не сопоставлено | DataReceived |
Bluetooth Services Obex Файл Bluetooth.Services.Obex.dll содержит пространство имен Bluetooth.Services.Obex. Это базовая реализация OBEX, предоставляемая через класс ObexService. Этот класс обеспечивает абстрагированное представление спецификации Bluetooth OPP. Он содержит метод, помогающий подключаться к принимающему Bluetooth-устройству, посылать ему пакеты и отключаться от него. В табл. 3 перечислены API и соответствующие события, предоставляемые этим классом, а на рис. 2 демонстрируется его использование.
Табл. 3. Методы и соответствующие события из ObexService
Метод (с параметрами) | Сопоставленные события |
[static] GetDefaultForBluetoothDevice(Bluetooth.Core.Services.BluetoothDevice) | Никаких событий не сопоставлено |
ConnectAsync | Успех: DeviceConnected Неудача: ConnectionFailed |
SendFileAsync(Windows.Storage.IStorageFile) | Успех: ServiceConnected, DataTransferProgressed, DataTransferSucceeded, Disconnecting Disconnected
Неудача: ConnectionFailed DataTransferFailed |
AbortAsync | Aborted |
Рис. 2. Применение сервиса Obex
protected async override void OnNavigatedTo(
Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ObexService obexService = ObexService.GetDefaultForBluetoothDevice(null);
obexService.DeviceConnected += obexService_DeviceConnected;
obexService.ServiceConnected += obexService_ServiceConnected;
obexService.ConnectionFailed += obexService_ConnectionFailed;
obexService.DataTransferProgressed += obexService_DataTransferProgressed;
obexService.DataTransferSucceeded += obexService_DataTransferSucceeded;
obexService.DataTransferFailed += obexService_DataTransferFailed;
obexService.Disconnecting += obexService_Disconnecting;
obexService.Disconnected += obexService_Disconnected;
obexService.Aborted += obexService_Aborted;
await obexService.ConnectAsync();
}
async void obexService_DeviceConnected(object sender, EventArgs e)
{
// Устройство подключено, теперь отправляем файл
await (sender as ObexService).SendFileAsync(fileToSend);
}
void obexService_ServiceConnected(object sender, EventArgs e)
{
// Устройство подключено к сервису Obex на целевом устройстве
}
void obexService_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
{
// Подключение к сервису потерпело неудачу
}
void obexService_DataTransferProgressed(object sender,
DataTransferProgressedEventArgs e)
{
// Получаем прогресс передачи данных от DataTransferProgressedEventArgs
}
void obexService_DataTransferSucceeded(object sender, EventArgs e)
{
// Передача данных была успешной
}
void obexService_DataTransferFailed(object sender,
DataTransferFailedEventArgs e)
{
// Передача данных закончилась неудачей, узнаем причину
// из DataTransferFailedEventArgs
}
void obexService_Disconnecting(object sender, EventArgs e)
{
// Устройство отключается от сервиса
}
void obexService_Disconnected(object sender, EventArgs e)
{
// Устройство теперь отключено от целевого устройства и сервиса
}
void obexService_Aborted(object sender, EventArgs e)
{
// Операция передачи данных отменена
}
Типичный сценарий использования этих библиотек примерно следующий.
- Используя API в Bluetooth.Core.Service, подсчитываем все сопряженные Bluetooth-устройства, поддерживающие OPP. Проверка на поддержку OPP уже реализована.
- Получаем экземпляр BluetoothDevice, с которыми вы хотите поделиться файлом.
- Получаем экземпляр ObexService для принимающего Bluetooth-устройства, передавая экземпляр BluetoothDevice в метод фабрики. Класс ObexService на внутреннем уровне создаст экземпляр BluetoothSocket, через который ObexService отправит файл.
- По окончании передачи файла ObexService автоматически отключается.
Приступаем к работе
Поскольку моя библиотека ориентирована на приложения Windows Store и Windows Phone 8.1, я начну с создания универсального приложения. Это отличный способ разработки приложений для всех Windows-устройств. Чтобы узнать больше об универсальных приложениях, зайдите на bit.ly/1h3AQeu. Я использую Visual Studio 2013 и создаю новый проект универсальных приложений в узле Store Apps (рис. 3). Я писал на Visual C#, но вы можете применить Visual Basic и Visual C++.
Рис. 3. Создание нового проекта с пустым универсальным приложением
Прежде чем приступить к программированию клиентского приложения Bluetooth OBEX, я обновлю файл package.appxmanifest для обоих проектов (приложений Windows 8.1 и Windows Phone 8.1):
<Capabilities>
<m2:DeviceCapability Name="bluetooth.rfcomm">
<m2:Device Id="any">
<m2:Function Type="name:obexObjectPush"/>
</m2:Device>
</m2:DeviceCapability>
</Capabilities>
Чтобы применить это обновление, я открываю package.appxmanifest как файл кода, выбрав View Code в контекстном меню в Solution Explorer. Я помещу этот фрагмент туда, где заканчивается тег <Application>. Этот фрагмент кода необходим, чтобы обеспечить аппаратную возможность использовать сервис RFCOMM (Bluetooth radio frequency communication) в этом приложении. Я также указал все сервисы и типы устройств, совместимые с этим устройством.
В моем сценарии требуется поддержка obexObjectPush от устройства, совместимого с любым устройством, поддерживающим OPP. Подробнее о поддерживаемых профилях в Windows 8.1 и Windows Phone 8.1 см. по ссылке bit.ly/1pG6rYO. Если эта возможность не упоминается, значит, перечисление устройств завершится неудачей с возвратом константы CapabilityNotDefined.
Перед кодированием я добавлю ссылки на три файла библиотек, упомянутые ранее, чтобы у меня была возможность использовать реализацию OBEX в этих библиотеках. Я должен добавить ссылки на эти библиотеки отдельно в обоих проектах. Если ссылка не добавлена, проект не сможет задействовать функциональность библиотек.
Я придерживаюсь следующих шаблонов и практик кодирования:
- реализация дизайна UX для приложения Windows Store 8.1 в Windows-проекте;
- реализация дизайна UX для приложения Windows Phone 8.1 в проекте Windows Phone;
- реализация общей функциональности в проекте Shared;
- реализация аппаратно-зависимой функциональности в проекте Shared, используя специфичные для конкретных платформ константы компиляции. В Windows 8.1 я буду применять WINDOWS_APP, а в Windows Phone 8.1 — WINDOWS_PHONE_APP. Эти константы уже определены как часть проекта.
Скачайте код примера, чтобы прочувствовать работу с этими библиотеками, а также практики кодирования, которым вы должны следовать при разработке универсальных приложений. На рис. 4 показан образец проекта в окне Solution Explorer с его структурой файлов и шаблонов.
Рис. 4. Содержимое окна Solution Explorer для приложения BluetoothDemo
Перечисление сопряженных устройств
Прежде чем обмениваться файлами с сопряженным устройством, я должен увидеть список таких устройств и выбрать целевое из них. Я получу описатель экземпляра Bluetooth.Core.Services.BluetoothService, который представляет базовый сервис Bluetooth, обеспечиваемый моим устройством. Я получаю этот экземпляр с помощью статического метода GetDefault фабрики, поскольку на каждом устройстве доступен только один сервис Bluetooth.
Для перечисления я вызову метод SearchForPairedDevicesAsync. Этот метод начнет перечислять устройства, сопряженные моему устройству. В случае приложений Windows Store 8.1 я должен разрешить использование сопряженного устройства, чтобы получить перечисляемые устройства. Если я запрещу такое использование, это сопряженное устройство не будет перечисляться.
Если этот API преуспеет, он сгенерирует событие SearchForPairedDevicesSucceeded и извлечет набор сопряженных устройств из аргумента события. В ином случае будет сгенерировано событие SearchForPairedDevicesFailed с перечислимой константой в аргументе события, указывающей на неудачу. На рис. 5 показан код для перечисления устройств.
Рис. 5. Перечисление сопряженных устройств
protected async override void OnNavigatedTo(
NavigationEventArgs e)
{
base.OnNavigatedTo(e);
await EnumerateDevicesAsync();
}
public async Task EnumerateDevicesAsync()
{
BluetoothService btService = BluetoothService.GetDefault();
btService.SearchForPairedDevicesFailed +=
btService_SearchForPairedDevicesFailed;
btService.SearchForPairedDevicesSucceeded +=
btService_SearchForPairedDevicesSucceeded;
await btService.SearchForPairedDevicesAsync();
}
void btService_SearchForPairedDevicesSucceeded(object sender,
SearchForPairedDevicesSucceededEventArgs e)
{
(sender as BluetoothService).SearchForPairedDevicesFailed -=
btService_SearchForPairedDevicesFailed;
(sender as BluetoothService).SearchForPairedDevicesSucceeded
-= btService_SearchForPairedDevicesSucceeded;
this.cvBtDevices.Source = e.PairedDevices;
}
void btService_SearchForPairedDevicesFailed(object sender,
SearchForPairedDevicesFailedEventArgs e)
{
(sender as BluetoothService).SearchForPairedDevicesFailed -=
btService_SearchForPairedDevicesFailed;
(sender as BluetoothService).SearchForPairedDevicesSucceeded
-= btService_SearchForPairedDevicesSucceeded;
txtblkErrorBtDevices.Text = e.FailureReason.ToString();
}
Я также предоставил кнопку сканирования в BottomAppBar для Windows 8.1 и ApplicationBar для Windows Phone 8.1. Тем самым пользователь приложения может повторно просканировать устройства при появлении нового сопряженного устройства.
При перечислении устройств в Windows Phone 8.1 будут перечисляться все устройства с поддержкой OBEX независимо от их физического присутствия и того, включен ли Bluetooth. Однако, когда перечисляются устройства в Windows 8.1, будет выдан список только тех устройств, которые физически присутствуют поблизости от устройства с Windows 8.1 и у которых Bluetooth включен.
Перечислив сопряженные устройства, я могу выбрать устройства для обмена с ним файлами. Каждое устройство представлено как объект класса Bluetooth.Core.Services.BluetoothDevice. Этот объект содержит детальные сведения о подключении и отображаемое название сопряженного устройства. Bluetooth.Services.Obex.ObexService будет использовать эти детальные сведения на внутреннем уровне для создания экземпляра Bluetooth.Core.Sockets.BluetoothSocket и подключения к сопряженному устройству.
Использование ObexService
Получив экземпляр объекта Bluetooth.Core.Services.BluetoothDevice, который представляет мое целевое устройство для обмена файлами, я могу задействовать Bluetooth.Services.Obex.ObexService для передачи файлов с помощью OPP. Мне также нужен список файлов, чтобы я мог поставить их в очередь для передачи. В примере кода я уже предоставил несколько файлов. В ином случае я мог бы использовать Windows.Storage.Pickers.FileOpenPicker (bit.ly/1qtiLeh) или собственную логику из моего Windows.Storage.ApplicationData.Current.LocalFolder (bit.ly/1qtiSGI) для выбора нескольких файлов.
На данный момент я имею возможность передавать лишь по одному файлу через каждое соединение. Когда передача заканчивается, соединение с целевым устройством закрывается. Если мне нужно отправить несколько файлов, я должен получать описатель экземпляра Bluetooth.Services.Obex.ObexService несколько раз. Я могу получить этот экземпляр через статический метод фабрики GetDefaultForBluetoothDevice(Bluetooth.Core.Services.BluetoothDevice). Этот метод возвращает один экземпляр Bluetooth.Services.Obex.ObexService, который представляет сервис Obex на устройстве.
Для представления передаваемого файла я буду использовать класс FileItemToShare. Он содержит имя, путь и размер файла, а также экземпляр Windows.Storage.IStorageFile (bit.ly/1qMcZlB), представляющий экземпляр файла на диске. Я буду ставить все передаваемые файлы в очередь как объекты структур данных. При передаче нескольких файлов первым в списке является тот файл, который передается в данный момент. Он удаляется из списка по окончании передачи. На рис. 6 показано, как подключить ObexService и его события для передачи файлов.
Рис. 6. Подключение ObexService и его событий
ObexService obexService = null;
BluetoothDevice BtDevice = null;
ObservableCollection<FileItemToShare> filesToShare = null;
async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter == null || !(e.Parameter is BluetoothDevice))
{
MessageDialog messageBox = new MessageDialog(
"Invalid navigation detected. Moving to Main Page",
"Bluetooth Hub");
messageBox.Commands.Add(new UICommand("OK", (uiCommand) =>
{
this.Frame.Navigate(typeof(MainPage));
}));
await messageBox.ShowAsync();
return;
}
BtDevice = e.Parameter as BluetoothDevice;
filesToShare = GetFilesFromLocalStorage();
this.cvFileItems.Source = filesToShare;
obexService =
ObexService.GetDefaultForBluetoothDevice(BtDevice);
obexService.Aborted += obexService_Aborted;
obexService.ConnectionFailed += obexService_ConnectionFailed;
obexService.DataTransferFailed +=
obexService_DataTransferFailed;
obexService.DataTransferProgressed +=
obexService_DataTransferProgressed;
obexService.DataTransferSucceeded +=
obexService_DataTransferSucceeded;
obexService.DeviceConnected += obexService_DeviceConnected;
obexService.Disconnected += obexService_Disconnected;
obexService.Disconnecting += obexService_Disconnecting;
obexService.ServiceConnected += obexService_ServiceConnected;
await obexService.ConnectAsync();
}
Когда я вызываю метод ConnectAsync, объект ObexService получает свойства соединения из объекта BluetoothDevice, который был передан методу фабрики. Он пытается создать соединение с целевым BluetoothDevice по каналу Bluetooth. В случае успеха генерируется событие DeviceConnected. На рис. 7 показан обработчик события DeviceConnected в ObexService.
Рис. 7. Метод-обработчик события DeviceConnected
async void obexService_DeviceConnected(
object sender, EventArgs e)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
System.Diagnostics.Debug.WriteLine("device connected");
if (filesToShare.Count > 0)
{
filesToShare.ShareStatus = FileShareStatus.Connecting;
await obexService.SendFileAsync(
filesToShare[0].FileToShare);
}
...
});
}
Как только устройство подключено к целевому устройству, я начинаю передачу файла с индекса 0 из списка файлов. Файл отправляется вызовом SendFileAsync(Windows.Storage.IStorageFile) с передачей объекта файла, представленного IStorageFile из объекта типа FileToShare. При вызове этого метода ObexService пытается подключиться к OBEX-серверу, выполняемому на целевом устройстве. Если подключение прошло успешно, генерируется событие ServiceConnected. В ином случае генерируется событие ConnectionFailed. Следующий код демонстрирует обработчик события ServiceConnected:
async void obexService_ServiceConnected(
object sender, EventArgs e)
{
await this.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
System.Diagnostics.Debug.WriteLine("service connected");
filesToShare[0].ShareStatus = FileShareStatus.Sharing;
});
}
На рис. 8 показан обработчик события ConnectionFailed в ObexService.
Рис. 8. Метод-обработчик события ConnectionFailed
async void obexService_ConnectionFailed(
object sender, ConnectionFailedEventArgs e)
{
await this.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, async () =>
{
System.Diagnostics.Debug.WriteLine("connection failed");
filesToShare[0].ShareStatus = FileShareStatus.Error;
filesToShare[0].Progress = 0;
FileItemToShare currentItem = filesToShare[0];
filesToShare.RemoveAt(0);
filesToShare.Add(currentItem);
});
}
Когда мое устройство подключается к OBEX-серверу целевого устройства, начинается процесс передачи файла. Прогресс передачи файлов можно определять по событию DataTransferProgressed. В следующем коде показан метод-обработчик DataTransferProgressed:
async void obexService_DataTransferProgressed(object sender,
DataTransferProgressedEventArgs e)
{
await this.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
System.Diagnostics.Debug.WriteLine("Bytes {0},
Percentage {1}",
e.TransferInBytes, e.TransferInPercentage);
filesToShare[0].Progress = e.TransferInBytes;
});
}
После успешной передачи файла генерируется событие DataTransferSucceeded. Если передача завершилась неудачей, генерируется событие DataTransferFailed. В следующем коде показан обработчик события DataTransferSucceeded:
async void obexService_DataTransferSucceeded(
object sender, EventArgs e)
{
await this.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
System.Diagnostics.Debug.WriteLine(
"data transfer succeeded");
filesToShare.RemoveAt(0);
});
}
Обработчик события DataTransferFailed приведен ниже:
async void obexService_DataTransferFailed(object sender,
DataTransferFailedEventArgs e)
{
await this.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, async () =>
{
System.Diagnostics.Debug.WriteLine(
"Data transfer failed {0}",
e.ExceptionObject.ToString());
filesToShare[0].ShareStatus = FileShareStatus.Error;
filesToShare[0].Progress = 0;
FileItemToShare fileToShare = this.filesToShare[0];
filesToShare.RemoveAt(0);
this.filesToShare.Add(fileToShare);
});
}
Когда передача заканчивается, соответствующий файл удаляется из списка, и ObexService отключается. При отключении ObexService генерируется событие Disconnecting. Обработчик события Disconnecting выглядит так:
void obexService_Disconnecting(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("disconnecting");
}
После успешного закрытия соединения код на рис. 9 генерирует событие Disconnected.
Рис. 9. Метод-обработчик события Disconnected
async void obexService_Disconnected(object sender, EventArgs e)
{
await this.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, async () =>
{
System.Diagnostics.Debug.WriteLine("disconnected");
obexService.Aborted -= obexService_Aborted;
obexService.ConnectionFailed -=
obexService_ConnectionFailed;
obexService.DataTransferFailed -=
obexService_DataTransferFailed;
obexService.DataTransferProgressed -=
obexService_DataTransferProgressed;
obexService.DataTransferSucceeded -=
obexService_DataTransferSucceeded;
obexService.DeviceConnected -= obexService_DeviceConnected;
obexService.Disconnected -= obexService_Disconnected;
obexService.Disconnecting -= obexService_Disconnecting;
obexService.ServiceConnected -=
obexService_ServiceConnected;
obexService = null;
if (filesToShare.Count.Equals(0))
{
...
MessageDialog messageBox =
new MessageDialog("All files are shared successfully",
"Bluetooth DemoApp");
messageBox.Commands.Add(new UICommand(
"OK", (uiCommand) =>
{
this.Frame.Navigate(typeof(MainPage));
}));
await messageBox.ShowAsync();
}
else
{
obexService = ObexService.GetDefaultForBluetoothDevice(
BtDevice);
obexService.Aborted += obexService_Aborted;
obexService.ConnectionFailed +=
obexService_ConnectionFailed;
obexService.DataTransferFailed +=
obexService_DataTransferFailed;
obexService.DataTransferProgressed +=
obexService_DataTransferProgressed;
obexService.DataTransferSucceeded +=
obexService_DataTransferSucceeded;
obexService.DeviceConnected +=
obexService_DeviceConnected;
obexService.Disconnected += obexService_Disconnected;
obexService.Disconnecting += obexService_Disconnecting;
obexService.ServiceConnected +=
obexService_ServiceConnected;
await obexService.ConnectAsync();
}
});
}
При генерации события Disconnected удаляйте все обработчики и очищайте экземпляр ObexService. В процессе передачи данных могут возникнуть такие условия, которые вынудят вас отменить текущую передачу. Чтобы отменить текущую передачу, вызовите AbortAsync. При этом генерируется событие Aborted (см. код ниже), а затем соединение с целевым устройством закрывается:
async void obexService_Aborted(object sender, EventArgs e)
{
await this.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
System.Diagnostics.Debug.WriteLine("Aborted");
if (!filesToShare.Count.Equals(0))
{
filesToShare.RemoveAt(0);
}
});
}
Заключение
Теперь демонстрационное приложение готово. Концепция универсальных приложений может реально помочь вам писать один кусок кода для нескольких платформ Windows и устройств с разнообразными форм-факторами, тем самым уменьшая общие затраты на разработку.
Я использовал эти библиотеки в ряде приложений Windows Store и Windows Phone. Найдите Code Create (бесплатное приложение Windows Phone) или OBEX (универсальное приложение) и посмотрите, как эти API работают совместно с приложением. Эти библиотеки доступны для скачивания из репозитария NuGet. Просто введите для поиска строку «Bluetooth OBEX for Store Apps» в онлайновом диалоге NuGet непосредственно из решения Visual Studio и импортируйте библиотеки в проекты как ссылку.