Windows Phone 8–Proximity與NFC–2
Windows Phone 8支持Proximity communication採用的是NFC技術。透過該技術讓二個設備之間透過tapping後建立連線。
在前一篇<Windows Phone 8–Proximity與NFC–1>說明了WP8在Proximity APIs後,該篇主要針對實作部分加以說明如何
操作Proximity APIs來交易與建立連線。
〉利用NFC來交換資訊 - device之間互相的溝通:
根據在前一篇討論的部分,在操作Proximity APIs最重要的類別:ProximityDevice是往下說明的重要部分,
包括操作設備是否具有Proximity provider、處理publish與subscribe運作、指定message type與儲存資訊,
另外在與Device互相溝通時使用的Protocol與callback handler也需要注意使用。往下便實際透過範例程式加以說明。
[事件準備]
1. 請先準備Windows Phone 8設備如果是要採用Emulator的話,請先參考<Proximity tapper>安裝相關功能。
2. 專案中的WMAppManifest.xml加上對應的<Capabilities />:ID_CAP_PROXIMITY。
針對要實作devices之間的互相溝通,首先從Publish / Subscribe使用的Message type與適用對象來找出實作的對象:
Protocol | Targets | Operations | Description |
Windows.{Sub type} | Between devices | Publish and subscribe | 訊息包括binary data。{Sub type}要記得指定。訊息代表被傳遞至另一個設備。 |
WindowsUri | Between devices | Publish and subscribe | 訊息包括一個UTF-16LE編碼的URI字串。不需指定{Sub type}。 |
WindowsMime.{MIME} | Between devices | Publish and subscribe | 訊息是指定的MIME類型。{MIME}要記得指定。 |
NDEF | Between devices, between device and a tag | Publish and subscribe | 訊息內容是標準的NDEF record。使用NDEF為message type則取得的訊息使用NDEF record。 |
主要在負責devices之間溝通有上述四個Protocol,以下範例將分別說明這四個Protocol的應用。
(1) 發佈/訂閱 Windows.MySubType 訊息:
1-1. 發佈訊息:
使用方法為:(此範例使用Message為主,如果要傳送BinaryMessage或UriMessage可自行轉換)
public long PublishMessage(string messageType, string message, MessageTransmittedHandler messageTransmittedHandler)
private void WindowsSubTypePublish()
{
// 回傳一個id做為建立subscriptione與stoppublishingmessage()
gPublishId = gDevice.PublishMessage(
"Windows.MySubType",
txtMsg.Text.Trim(),
// 增加該handler負責處理當發佈的訊息已抵達至另一個設備。
(ProximityDevice device, long messageId) =>
{
Dispatcher.BeginInvoke(() =>
{
txtLog.Text = string.Format("已傳送訊息:id = {0}\r\n", messageId);
});
CancelPublication();
});
}
搭配參數與實作對應的Handler:
public delegate void MessageTransmittedHandler(ProximityDevice sender, long messageId)
負責處理當發佈的訊息送達另一個設備所觸發的事件。至於在發送時的Message Type請參考<>或<>的說明。
1-2. 訂閱訊息:
使用方法為:
public long SubscribeForMessage(string messageType, MessageReceivedHandler messageReceivedHandler)
private void WindowsSubTypeSubsrcibe()
{
// 指定ProximityDevice訂閱某個訊息類型的訊息
// 並指定收到訊息後要處理的handler
gSubscribeId = gDevice.SubscribeForMessage(
"Windows.MySubType",
(ProximityDevice device, ProximityMessage message) =>
{
Dispatcher.BeginInvoke(() =>
{
// 因為收到的訊息為字串
txtLog.Text = string.Format("已收到訊息:\r\nmsg type:{0}\r\ncontent:{1}\r\nid:{2}",
message.MessageType,
message.DataAsString,
message.SubscriptionId);
});
CancelSubscritption();
});
}
搭配參數與實作對應的Handler:
public delegate void MessageReceivedHandler(ProximityDevice sender, ProximityMessage message)
負責處理設備收到了指定訂閱的訊息所觸發的事件。
(2). 發佈/訂閱 WindowsUri 訊息:
2-1. 發佈URI messages:
發佈的訊息內容,例如:web links、telephone links、application specific URI schema…等,類似發佈文字訊息。
使用:PublishUriMessage()的方法,將訊息發佈給其他的devices;使用PublishBinaryMessage()的方法將訊息發佈至tag之中。
需注意使用的是:WindowsUri 協定,並且URI需使用UTF-16LE編碼;
private void PublishWindowsUri()
{
// 採用一個.PublishUriMessage發送指定的uri
gPublishId = gDevice.PublishUriMessage(
new Uri(txtMsg.Text.Replace("msg:",string.Empty)),
// 增加該handler負責處理當發佈的訊息已抵達至另一個設備。
(ProximityDevice sender, long messageId) =>
{
Dispatcher.BeginInvoke(() =>
{
txtLog.Text = string.Format("已傳送訊息:id = {0}\r\n", messageId);
});
CancelPublication();
});
}
2-2. 訂閱URI messages:
private void SubscribeWindowsUri()
{
gSubscribeId = gDevice.SubscribeForMessage(
"WindowsUri",
(ProximityDevice device, ProximityMessage message) =>
{
// 處理取得message的內容
byte[] array = new byte[message.Data.Length];
using (DataReader dataReader = DataReader.FromBuffer(message.Data))
{
dataReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
dataReader.ReadBytes(array);
Uri uri = new Uri(System.Text.Encoding.Unicode.GetString(
array, 0, array.Length - 2));
txtLog.Text = String.Format("Received URL \"{0}\"\n", uri.ToString());
CancelSubscritption();
}
});
}
(3) 發佈/訂閱 WindowsMime.{MIME} 訊息:
3-1. 發佈 WindowsMime.{MIME} 訊息:
以圖像png為例,發佈給另一個設備。在發佈時需注意只適用於小size的圖示、contact、行事曆事件…等。另外,
也需要注意ProximityDevice的MaxMessageBytes屬性,它限制了有多少bytes可以被發佈出來。建議在發送時先了解一下大小。
在處理MIME時,需根據發送的類型不同將它們轉成byte[],才能進行發佈。
private void WindowsMimePublish()
{
// 顯示目前ProximityDevice支持最大發佈的Size
MessageBox.Show(string.Format("Max sizx: {0} KB", gDevice.MaxMessageBytes / 1024));
// 指定要傳遞的檔案
Uri tUri = new Uri("Assets/ApplicationIcon.png", UriKind.Relative);
using (Stream tStream = System.Windows.Application.GetResourceStream(tUri).Stream)
{
byte[] tBytes = new byte[tStream.Length];
tStream.Read(tBytes, 0, (int)tStream.Length);
using (DataWriter tWriter = new DataWriter())
{
// PNG specification says that data must be in MSB order
tWriter.ByteOrder = ByteOrder.BigEndian;
tWriter.WriteBytes(tBytes);
// to publish to a tag you would use message type "WindowsMime:WriteTag.image/png"
gPublishId = gDevice.PublishBinaryMessage(
"WindowsMime.image/png",
tWriter.DetachBuffer(),
(ProximityDevice device, long messageId) =>
{
Dispatcher.BeginInvoke(() =>
{
txtLog.Text = string.Format("已傳送訊息:id = {0}\r\n", messageId);
});
});
}
}
}
3-2. 訂閱 WindowsMime.{MIME} 訊息:
private void WindowsMimeSubscribe()
{
gSubscribeId =
gDevice.SubscribeForMessage(
"WindowsMime.image/png",
(ProximityDevice device, ProximityMessage message) =>
{
byte[] array = new byte[message.Data.Length];
using (DataReader dataReader = DataReader.FromBuffer(message.Data))
{
// PNG specification
dataReader.ByteOrder = ByteOrder.BigEndian;
dataReader.ReadBytes(array);
Dispatcher.BeginInvoke(() =>
{
using (MemoryStream stream = new MemoryStream(array))
{
var bitmap = new System.Windows.Media.Imaging.BitmapImage();
bitmap.SetSource(stream);
// you could have the following image declared in XAML
imgObj.Source = bitmap;
}
});
}
});
}
相關WindowsMime的資訊可以參考:<WindowsMime Protocol>。
另外,如果您希望透過NFC實現devices之間檔案的傳遞,那麼您可用 PeerFinder 類別來加以實現,概念是:
透過NFC找到new peer(使用TriggeredConnectionStateChanged 事件或subscribe 一個app-to-app的NFC message),
接著透過PeerFinder建立一個StreamSocket物件來實現二個devices之間資料的傳遞。(ProximityDevice)
4. 發佈/訂閱 NDEF 訊息:
NFC Forum已定義了NFC交易內容專用的binary structure,命名為:NDEF (NFC Data Exchange Format)。
4-1. 發佈 NDEF 訊息:
由於這邊使用的是devices之間的交易,所以用「NDEF」為message type;針對device與tag則要改成「NDEF:WriteTag」。
由於NDEF record不容易編輯,因此有了<NDEF Library for Proximity APIs (NFC)>,透過它來往下操作。
private void PublishNDEFMessage()
{
// 使用NDEF Library元件來發送個 TEXT訊息
var ndefRecord = new NdefTextRecord
{
Text = "hello, NFC",
LanguageCode = CultureInfo.CurrentCulture.TwoLetterISOLanguageName
};
var ndefMessage = new NdefMessage { ndefRecord };
gPublishId = gDevice.PublishBinaryMessage(
"NDEF",
ndefMessage.ToByteArray().AsBuffer(),
(ProximityDevice device, long messageId) =>
{
// 標籤已寫入資訊,停止發佈。
CancelPublication();
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("transmitting device succed");
});
});
}
利用NdefTextRecord類別為例, 簡單寫入一個訊息來操作訊息。
4-2. 訂閱 NDEF 訊息:
private void SubscribeNDEFMessage()
{
gSubscribeId = gDevice.SubscribeForMessage(
"NDEF",
(ProximityDevice device, ProximityMessage message) =>
{
string tMsg = string.Empty;
var ndefMessage = NdefMessage.FromByteArray(message.Data.ToArray());
foreach (NdefRecord record in ndefMessage)
{
// 取得序列化類型,進一步識別為何種NDEF類型
var specializedType = record.CheckSpecializedType(false);
if (specializedType == typeof(NdefTextRecord))
{
// Convert and extract Smart Poster info
var textRecord = new NdefTextRecord(record);
tMsg += "Type: " + textRecord.Type.ToString() + " " + textRecord.LanguageCode + "\n";
tMsg += "Content: " + textRecord.Text + "\n";
}
}
Dispatcher.BeginInvoke(() =>
{
txtLog.Text = tMsg;
});
});
}
5. 發佈/訂閱 Custom 的訊息:
要透過自訂的類別可以做為Proximity的交易,首先需要先定義一個可被序列化的類別,接著在透過序列化與反序列化的方式,
定義好的客製類別進行傳遞與交易。
5-1. 定義自訂類別:
[System.Runtime.Serialization.DataContract]
public class MyStoreInfo
{
[System.Runtime.Serialization.DataMember]
public string Name { get; set; }
[System.Runtime.Serialization.DataMember]
public string Address { get; set; }
[System.Runtime.Serialization.DataMember]
public string URI { get; set; }
}
在操作定義類別時,如果遇到類別無法定義,記得加入 System.Runtime.Serialization.dll 的參考。
5-2. 發佈自訂類別:
private void PublishCustomData()
{
Custom.MyStoreInfo tInfo = new Custom.MyStoreInfo
{
Name = "吃到飽",
Address = "台北市火車站",
URI = "http://www.google.com"
};
// 將Custom Data的類別序列化
MemoryStream stream = new MemoryStream();
var serializer = new System.Runtime.Serialization.DataContractSerializer(typeof(Custom.MyStoreInfo));
serializer.WriteObject(stream, tInfo);
// 將取得的Stream轉成byte[],最後再透過datawriter寫入proximity
byte[] array = new byte[stream.Length];
stream.Position = 0;
stream.Read(array, 0, (int)stream.Length);
using (DataWriter tWriter = new DataWriter())
{
tWriter.WriteBytes(array);
// 發佈自訂的SubType:Windows.MyCustom:ProximityProj.Custom
gPublishId = gDevice.PublishBinaryMessage(
"Windows.MyCustom:ProximityProj.Custom",
tWriter.DetachBuffer(),
(ProximityDevice device, long messageId) =>
{
// 標籤已寫入資訊,停止發佈。
CancelPublication();
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("Write tag succed");
});
});
}
}
5-3. 訂閱自訂類別:
private void SubscribeCustomData()
{
gSubscribeId = gDevice.SubscribeForMessage(
"Windows.MyCustom:ProximityProj.Custom",
(ProximityDevice device, ProximityMessage message) =>
{
byte[] array = new byte[message.Data.Length];
using (DataReader dataReader = DataReader.FromBuffer(message.Data))
{
dataReader.ByteOrder = ByteOrder.BigEndian;
dataReader.ReadBytes(array);
using (MemoryStream memory = new MemoryStream(array))
{
var serializer = new System.Runtime.Serialization.DataContractSerializer(typeof(Custom.MyStoreInfo));
Custom.MyStoreInfo item = (Custom.MyStoreInfo)serializer.ReadObject(memory);
Dispatcher.BeginInvoke(() =>
{
string tMsg = string.Format("name: {0}\naddress: {1}\nuri: {2}",
item.Name, item.Address, item.URI);
MessageBox.Show(tMsg);
});
}
}
});
}
操作時DataReader、DataWriter是最重要的類別,另外要注意Stream的操作也是一個學問。
[範例程式]
測試方法:
‧請將WMAppManifest.xml中的<DefaultTask />調整成MainPage.xaml即可以進行測試。
‧測試Windows.{Sub type}:在文字框輸入:「msg:」
‧測試WindowsUri:在文字框輸入:「uri:」
‧測試WindowsMime.{mime type}:在文字框輸入:「mime:」
‧測試NDEF:在文字框輸入:「ndef:」
‧測試Custom Data:在文字框輸入:「custom:」
======
"Proximity"並非只有NFC技術而已,只是WP 8與其他系統之間近期以支持NFC這higher level的技術。
以上該篇主要說明devices之間的溝通,相關於device與tag之間的操作,則參考下一篇<Windows Phone 8–Proximity與NFC–3>的內容。
希望有助於大家了解Proximity APIs的功能,謝謝。
References:
‧Proximity and Near Field Communication & NFC (重點文章,其內容包括很多相關的範例)
‧Using NFC to exchange information (重要)
‧Using NFC to establish a persistent connection
‧How to Launch Apps via Proximity APIs (NFC)
‧New NFC and Proximity documentation on Windows Phone 8
‧Windows Phone 8: Networking, Bluetooth, and NFC Proximity for Developers & PixPresenter Sample (重要)
‧WP8 NFC Tutorial: Voice Messages on NFC Tags
‧How to Store Application Data on NFC Tags
‧Use NFC tags with Windows Phone 8 (重要)
‧http://ndef.codeplex.com/ (存取NDEF必要) & PublishBinaryMessage()
‧Windows.Storage.Streams namespace
‧Calling the API PublishBinaryMessage will fail when handling NFC touch case
‧What is the best way to update an XAML image element with a new JPEG image?
‧Connecting Android and Windows 8 via NFC
‧10 Great Windows Phone 8 Code Samples (No, Wait… 30)