《深入淺出Windows Phone 8應用開發》之藍牙程式設計
藍牙是手機的近距離無限傳輸的技術,在之前的Windows Phone 7系統手機裡面僅支援藍牙耳機功能,并不支援藍牙檔案資訊傳輸,那麼在Windows Phone 8手機裡面将全面支援藍牙技術,并且提供了相關的API來給開發者使用。開發者可以利用藍牙的相關API來建立應用程式,在應用程式裡面使用手機的藍牙技術來進行近距離的檔案傳輸和發送接收消息,創造出更加有趣和友善的應用軟體。
在Windows Phone 8裡面可以在應用程式裡面利用藍牙進行通信,使用藍牙相關的API,可以讓應用程式連接配接到另外的一個應用程式,也可以讓應用程式連接配接到一個裝置上。Windows Phone 8的藍牙技術支援兩個藍牙方案:一個是應用程式到應用程式的通信,另外一個是應用程式到裝置的通信。
1.應用程式到應用程式的通信
應用程式到應用程式的通信的過程是,應用程式使用藍牙去查找正在廣播藍牙服務的對等的應用程式,如果在應用程式提供服務的範圍内發現一個應用程式,那麼該應用程式可以發起連接配接請求。當這兩個應用程式接受連接配接,它們之間就可以進行通信了,通信的過程是使用socket的消息發送接收機制。在Windows Phone 8中使用到應用程式到應用程式的藍牙通訊技術,需要在項目的WMAppManifest.xml檔案中添加ID_CAP_PROXIMITY的功能選項,表示支援臨近的裝置通信能力,否則程式會出現異常。
2.應用程式到裝置的通信
在應用程式到裝置的通信過程時,應用程式使用藍牙去查找提供服務的裝置,如果提供的服務範圍之内發現一個可以連接配接的藍牙裝置,那麼該應用程式可以發起連接配接請求。當應用程式和裝置同時接受該連接配接,它們之間就可以進行通信了,通信的過程也是使用socket的消息發送接收機制,類似于應用程式到應用程式的通信。在Windows Phone 8中使用到應用程式到裝置的藍牙通訊技術,需要在項目的WMAppManifest.xml檔案中添加ID_CAP_PROXIMITY和ID_CAP_NETWORKING的功能選項,表示支援臨近的裝置通信能力和網絡通信能力,否則程式會出現異常。
在Windows Phone 8裡面使用到藍牙程式設計主要會用到PeerFinder類,PeerInformation類,StreamSocket類和ConnectionRequestedEventArgs類,這些類的說明如表19.1所示。因為藍牙也是基于TCP協定進行消息傳遞了,是以需要用到Socket的相關的程式設計知識,以及StreamSocket類。PeerFinder類是藍牙查找類,它的主要成員如表19.2所示。
表19.1 藍牙程式設計類的說明
類名
說明
PeerFinder
用于去查找附近的裝置是否有運作和目前應用程式相同的應用程式,并且可以在兩個應用程式之間建立起socket連接配接,進而可以進行通信。對等應用程式是在其他裝置上運作的應用程式的另一個執行個體。
PeerInformation
包含對等應用程式或裝置的識别資訊。
StreamSocket
支援使用一個TCP的Socket流的網絡通信。
ConnectionRequestedEventArgs
表示傳遞到一個應用程式的ConnectionRequested事件的屬性
表 19.2 PeerFinder類的成員
成員
bool AllowBluetooth
指定 PeerFinder 類的此執行個體是否可以通過使用 Bluetooth 來連接配接 ProximityStreamSocket 對象。如果PeerFinder 的此執行個體可以通過使用 Bluetooth 來連接配接 ProximityStreamSocket 對象,則為 true;否則為false。預設為 true。
bool AllowInfrastructure
是否使用TCP/IP協定連接配接到StreamSocket
bool AllowWiFiDirect
指定 PeerFinder 類的此執行個體是否可以通過使用 Wi-Fi Direct 來連接配接 ProximityStreamSocket 對象。如果 PeerFinder 的此執行個體可以通過使用 Wi-Fi Direct 來連接配接 ProximityStreamSocket 對象,則為 true;否則為false。預設為 true。
IDictionary<string, string> AlternateIdentities
擷取要與其他平台上的對等應用程式比對的備用 AppId 值清單。傳回要與其他平台的對等類應用程式比對的備用 AppId 值清單。
string DisplayName
擷取或設定辨別計算機到遠端對等類的名稱。
PeerDiscoveryTypes SupportedDiscoveryTypes
擷取一個值,該值訓示哪些發現選項可與 PeerFinder 類一同使用
event TypedEventHandler<object, ConnectionRequestedEventArgs> ConnectionRequested
遠端對等類使用 ConnectAsync 方法請求連接配接時發生。
event TypedEventHandler<object, TriggeredConnectionStateChangedEventArgs> TriggeredConnectionStateChanged
在遠端對等類的輕擊筆勢期間發生。
IAsyncOperation< StreamSocket> ConnectAsync(PeerInformation peerInformation)
連接配接已發現了對 FindAllPeersAsync 方法的調用的對等類。peerInformation:表示連接配接到的對等類的對等類資訊對象。傳回通過使用所提供的臨近StreamSocket 對象連接配接遠端對等類的異步操作。
IAsyncOperation<IReadOnlyList<PeerInformation>> FindAllPeersAsync()
适用于無線範圍内運作相同應用程式的對等計算機的異步浏覽。傳回通過使用 Wi-Fi直連技術浏覽對等類的異步操作。
void Start(string peerMessage)
向臨近裝置上的對等類應用程式傳遞消息。
void Stop()
停止查找對等類應用程式或廣播對等類連接配接的過程
查找在服務範圍内的藍牙裝置和對等項是藍牙程式設計的第一步,查找藍牙裝置和對等項中會使用到PeerFinder類的FindAllPeersAsync方法去進行查找,然後以異步的方式傳回查找到的對等項清單的資訊IReadOnlyList<PeerInformation>,注意要使查找對等的應用程式時,在調用FindAllPeersAsync方法前必須先調用PeerFinder類的Start方法,主要的目的是啟動廣播服務,讓對方的應用程式也能查找到自己。PeerInformation包含三個屬性:一個是DisplayName表示對等項的名字,這個名字一般都是由對方的裝置的名稱或者查找到的應用程式自身設定的現實名字,一個是HostName表示主機名字或者IP位址,還有一個屬性是ServiceName表示服務名稱或者TCP協定的端口号。然後可以利用查找到的PeerInformation資訊進行連接配接和通信。
查找對等的應用程式的代碼示例:
async void AppToApp()
{
// 啟動查找服務
PeerFinder.Start();
//開始查找
ObservableCollection<PeerInformation> peers = await PeerFinder.FindAllPeersAsync();
if (peers.Count == 0)
{
//未找到任何的對等項
}
else
//處理查找到的對等項,可以使用PeerFinder類的ConnectAsync方法來連接配接選擇的要進行通信的對等項
}
查找藍牙裝置的代碼示例:
private async void AppToDevice()
// 設定查找所比對的藍牙裝置
PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
// 開始查找
ObservableCollection<PeerInformation> pairedDevices = await PeerFinder.FindAllPeersAsync();
if (pairedDevices.Count == 0)
// 沒有找到可用的藍牙裝置
//處理查找到的藍牙裝置,可以建立一個StreamSocket對象,然後使用StreamSocket類的ConnectAsync方法通過HostName和ServiceName來連接配接藍牙裝置
藍牙程式設計的發送消息機制使用的是TCP的StreamSocket的方式,原理與Socket的一緻。在藍牙連接配接成功後,可以擷取到一個StreamSocket類的對象,然後我們使用該對象的OutputStream屬性來初始化一個DataWriter對象,通過DataWriter對象來進行發送消息。OutputStream屬性表示的是Socket的輸出流,用于發送消息給對方。下面來看一下發送消息的示例:
async void SendMessage(string message)
// 連接配接選中的對等項,selectedPeer為查找到的PeerInformation對象
StreamSocket _socket= = await PeerFinder.ConnectAsync(selectedPeer);
// 建立DataWriter
DataWriter _dataWriter = new DataWriter(_socket.OutputStream);
// 先寫入發送消息的長度
_dataWriter.WriteInt32(message.Length);
await _dataWriter.StoreAsync();
// 最後寫入發送消息的内容
_dataWriter.WriteString(message);
藍牙程式設計的接收消息機制同樣也是使用的是TCP的StreamSocket的方式,原理與Socket的一緻。在藍牙連接配接成功後,可以擷取到一個StreamSocket類的對象,然後我們使用該對象的InputStream屬性來初始化一個DataReader對象,通過DataReader對象來進行接收消息。InputStream屬性表示的是Socket的輸入流,用于接收對方的消息。下面來看一下接收消息的示例:
async Task<string> GetMessage()
// 建立DataReader
DataReader _dataReader = new DataReader(_socket.InputStream);
// 先讀取消息的長度
await _dataReader.LoadAsync(4);
uint messageLen = (uint)_dataReader.ReadInt32();
// 最後讀取消息的内容
await _dataReader.LoadAsync(messageLen);
return _dataReader.ReadString(messageLen);
下面給出藍牙程式對程式傳輸的示例:通過使用藍牙功能查找周邊也要使用改應用的手機,互相建立起連接配接和發送測試消息。
代碼清單19-1:藍牙程式對程式傳輸(源代碼:第19章\Examples_19_1)
MainPage.xaml檔案主要代碼
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<Button x:Name="btFindBluetooth" Content="通過藍牙查找該應用裝置" Click="btFindBluetooth_Click"/>
<ListBox x:Name="lbBluetoothApp" ItemsSource="{Binding}" >
<ListBox.ItemTemplate >
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding DisplayName}" />
<TextBlock Text="{Binding ServiceName}" />
<Button Content="連接配接" HorizontalAlignment="Left" Width="308" Height="91" Click="btConnect_Click"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
MainPage.xaml.cs檔案主要代碼
using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
namespace BluetoothDemo
public partial class MainPage : PhoneApplicationPage
private StreamSocket _socket = null; // Socket資料流對象
private DataWriter _dataWriter; // 資料寫入對象
private DataReader _dataReader; // 資料讀取對象
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;//頁面加載事件
}
// 查找藍牙對等項按鈕事件處理
private async void btFindBluetooth_Click(object sender, RoutedEventArgs e)
try
{
//開始查找對等項
PeerFinder.Start();
// 等待找到的對等項
var peers = await PeerFinder.FindAllPeersAsync();
if (peers.Count == 0)
{
MessageBox.Show("沒有發現對等的藍牙應用");
}
else
// 把對等項目綁定到清單中
lbBluetoothApp.ItemsSource = peers;
}
catch(Exception ex)
if ((uint)ex.HResult == 0x8007048F)
MessageBox.Show("Bluetooth已關閉請打開手機的藍牙開關");
MessageBox.Show(ex.Message);
// 連接配接藍牙對等項的按鈕事件處理
private async void btConnect_Click(object sender, RoutedEventArgs e)
Button deleteButton = sender as Button;
PeerInformation selectedPeer = deleteButton.DataContext as PeerInformation;
// 連接配接到選擇的對等項
_socket = await PeerFinder.ConnectAsync(selectedPeer);
// 使用輸出輸入流建立資料讀寫對象
_dataReader = new DataReader(_socket.InputStream);
_dataWriter = new DataWriter(_socket.OutputStream);
// 開始讀取消息
PeerFinder_StartReader();
// 讀取消息
async void PeerFinder_StartReader()
uint bytesRead = await _dataReader.LoadAsync(sizeof(uint));
if (bytesRead > 0)
// 擷取消息内容的大小
uint strLength = (uint)_dataReader.ReadUInt32();
bytesRead = await _dataReader.LoadAsync(strLength);
if (bytesRead > 0)
{
String message = _dataReader.ReadString(strLength);
MessageBox.Show("擷取到消息:" + message);
// 開始下一條消息讀取
PeerFinder_StartReader();
}
else
MessageBox.Show("對方已關閉連接配接");
MessageBox.Show("對方已關閉連接配接");
catch (Exception e)
MessageBox.Show("讀取失敗: " + e.Message);
// 頁面加載事件處理
void MainPage_Loaded(object sender, RoutedEventArgs e)
// 訂閱連接配接請求事件
PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;
// 連接配接請求事件處理
void PeerFinder_ConnectionRequested(object sender, ConnectionRequestedEventArgs args)
// 連接配接并且發送消息
ConnectToPeer(args.PeerInformation);
// 連接配接并發送消息給對方
async void ConnectToPeer(PeerInformation peer)
_socket = await PeerFinder.ConnectAsync(peer);
string message = "測試消息";
uint strLength = _dataWriter.MeasureString(message);
_dataWriter.WriteUInt32(strLength);//寫入消息的長度
_dataWriter.WriteString(message);//寫入消息的内容
uint numBytesWritten = await _dataWriter.StoreAsync();
}
程式的運作效果如圖19.2所示。

下面給出藍牙程式對裝置連接配接的示例:查找藍牙裝置,并對找到的第一個藍牙裝置進行連接配接。
代碼清單19-2:藍牙程式對裝置連接配接(源代碼:第19章\Examples_19_2)
<StackPanel>
<Button x:Name="btFindBluetooth" Content="連接配接周圍的藍牙裝置" Click="btFindBluetooth_Click"/>
</StackPanel>
// 查找藍牙裝置事件處理
private async void btFindBluetooth_Click(object sender, RoutedEventArgs e)
{
try
{
// 配置PeerFinder藍牙服務的GUID去搜尋裝置
PeerFinder.AlternateIdentities["Bluetooth:SDP"] = "5bec6b8f-7eba-4452-bf59-1a510745e99d";
var peers = await PeerFinder.FindAllPeersAsync();
if (peers.Count == 0)
{
Debug.WriteLine("沒發現藍牙裝置");
}
else
// 連接配接找到的第一個藍牙裝置
PeerInformation selectedPeer = peers[0];
StreamSocket socket = new StreamSocket();
await socket.ConnectAsync(selectedPeer.HostName, selectedPeer.ServiceName);
MessageBox.Show("連接配接上了HostName:" + selectedPeer.HostName + "ServiceName:" + selectedPeer.ServiceName);
}
catch (Exception ex)
if ((uint)ex.HResult == 0x8007048F)
MessageBox.Show("Bluetooth is turned off");
}
程式的運作效果如圖19.3所示
本文轉自linzheng 51CTO部落格,原文連結:http://blog.51cto.com/linzheng/1116578