[NFC] 發行(Publish)與訂閱(Subscribe)訊息 (Windows 8/ Windows Phone 8)(1)

[NFC] 發行(Publish)與訂閱(Subscribe)訊息

        要談到在 Windows 8/ Windows Phone 8 使用 NFC 發送與訂閱訊息前有一些觀念,第一點是目前的 API 只支援 NDEF (NFC Data Exchange Formate) 格式的資料,第二點是關於 Message Type。

 

 

        Message Type 由兩個部份組成 (1) Message Protocol (必備) (2) Sub Type (某些 Message Protocol 不需要 Sub Type 如 WindowsUri)。下表資料來自於 MSDN 文件中的ProximityDevice.PublishBinaryMessage

Message Protocol 說明
Windows 訊息包含二進位資料。
WindowsUri

訊息資料包含 UTF-16LE 編碼的 URI 字串。

Windows 一律藉由提示使用者檢視收到的 URI 來處理 "WindowsUri" 訊息 (在處理特定 URI 通訊協定的 Windows 預設應用程式中),例如,http:// URI 會在預設 Web 瀏覽器中開啟。Windows 會在預設應用程式中開啟 URI,即使有其他應用程式也訂閱 "WindowsUri"。

使用 PublishUriMessage 方法,而不是以這個通訊協定呼叫 PublishBinaryMessage 方法。

如果您將 URI 發佈至沒有執行 Windows 的電腦,則 URI 會自動根據您電腦支援的近接感測技術進行格式化。例如,如果您的電腦使用近接感測的 NFC 硬體,URI 會格式化為 NDEF URI 記錄。

如果您訂閱的是 "WindowsUri" 訊息,而且 Windows 電腦會接收電腦支援之近接感測技術所格式化的訊息,則 Windows 會讀取格式化的訊息,並且只傳回 URI 做為訊息內容。

WindowsMime

訊息資料是特定的 MIME 類型。例如,如果訊息資料是 jpeg 影像,則訊息類型為 "WindowsMime.image/jpeg"。

Windows 一律藉由提示使用者檢視收到的 MIME 內容來處理 "WindowsMime" 訊息,即使也已針對 MIME 內容訂閱應用程式。

如果您將 "WindowsMime" 訊息發佈至沒有執行 Windows 的電腦,則訊息內容會自動根據您電腦支援的近接感測技術進行格式化。例如,如果您的電腦使用近接感測的 NFC 硬體,訊息內容會格式化為 NDEF MIME 記錄。

如果您要發行 "WindowsMime" 訊息,您永遠都必須包含 MIME 類型。如果您訂閱的是 "WindowsMime" 訊息,則可以使用沒有指定 MIME 類型的 "WindowsMime" 來訂閱所有 MIME 類型訂閱。如果您訂閱的是特定 MIME 類型,而且 Windows 電腦會接收電腦支援之近接感測技術所格式化的訊息,則 Windows 會讀取格式化的訊息,並且只傳回 MIME 內容做為訊息內容。如果您訂閱 MIME 類型,則收到的前 256 個位元組會是 ASCII MIME 類型字串,而剩餘的位元組就是 MIME

Windows:WriteTag 這與 Windows 通訊協定相同,不過內容要寫入靜態標記。訊息不會傳送至可寫入靜態標記以外的任何裝置。這個通訊協定只對發行有效。例如,"Windows:WriteTag.SampleSubtype" 即是這樣的通訊協定。
WindowsUri:WriteTag 這與 WindowsUri 通訊協定相同,不過內容要寫入靜態標記。訊息不會傳送至可寫入靜態標記以外的任何裝置。這個通訊協定只對發行有效。
WindowsMime:WriteTag 這與 WindowsMime 通訊協定相同,不過內容要寫入靜態標記。訊息不會傳送至可寫入靜態標記以外的任何裝置。這個通訊協定只對發行有效。例如,"WindowsMime:WriteTag.image/jpeg" 即是這樣的通訊協定。
LaunchApp:WriteTag

撰寫可透過選擇性啟動參數來啟動特定應用程式的標記。如果您將 LaunchApp:WriteTag 訊息發佈至標記,則點選該標記時與呼叫 PeerFinder.Start 方法再傳遞啟動參數時產生至電腦的結果是一樣的。訊息必須是 UTF-16LE 編碼字串,其中的值依照下列形式以定位字元或 null 值分隔:

<launch arguments>[tab]<app platform 1>[tab]<app name 1>...[tab]<app platform N>

 

您至少必須指定一個應用程式平台和應用程式名稱。Windows 8 電腦的應用程式平台是 Windows。近接感測應用程式識別碼的格式是 <package family name>!<app Id>。您可以從 Windows.ApplicationModel.Package.Current.Id.FamilyName 屬性取得套件家族名稱。您必須從應用程式的套件資訊清單中 Application 項目的 Id 屬性中複製應用程式識別碼值。 例如,"user=default\tWindows\tExample.Proximity.JS_8wekyb3d8bbwe!Proximity.App" 即是這樣的訊息。

您也可以支援其他應用程式平台。如需詳細資訊,請參閱 AlternateIdentities

WriteableTag 訂閱這個訊息通訊協定時,如果放入對近接感測的可寫入標記,則接收的近接感測訊息會包含 int32 (由小到大),指出標記的最大可寫入大小。這個通訊協定只對訂閱有效。
Pairing:Bluetooth Windows 會訂閱這個訊息類型,以使用近接感測來完成 Bluetooth 配對。這個通訊協定主要不是在應用程式中使用。
NDEF 訊息內容是格式正確的 NDEF 記錄。使用 NDEF 做為訊息類型內容的發行物的基礎型別包含在 NDEF 記錄中。對 NDEF 類型的訂閱會訂閱所有 NDEF 格式訊息。
NDEF:ext 訊息資料是應用程式定義的 NDEF 記錄 (TNF 欄位值 0x04)。這個通訊協定只適用於訂閱,在發行 NDEF 內容時,請使用 NDEF
NDEF:MIME 訊息資料是格式正確的 NDEF MIME 訊息 (TNF 欄位值 0x02)。例如,"NDEF:MIME.image/jpeg"。這個通訊協定只適用於訂閱,在發行 NDEF 內容時,請使用 NDEF
NDEF:URI 訊息資料是 URI 命名空間所定義之型別、格式正確的 NDEF 訊息 (TNF 欄位值 0x03)。這個通訊協定只適用於訂閱,在發行 NDEF 內容時,請使用 NDEF。這表示資料格式是由指定的 URI 所識別。例如,"NDEF:URI.http://contoso.com/sometype" 即是這樣的通訊協定。
NDEF:wkt 訊息資料是 NFC 論壇所定義之型別、格式正確的 NDEF 訊息 (TNF 欄位值 0x01)。對於已知 URI 型別,這個型別的範例為 "NDEF:wkt.U"。這個通訊協定只適用於訂閱,在發行 NDEF 內容時,請使用 NDEF
NDEF:WriteTag 訊息資料應該寫入至 NFC 論壇標準靜態標記。訊息資料必須採用適當的 NDEF 格式。這個通訊協定只對發行有效。
NDEF:Unknown 訊息資料是不具型別 NDEF 訊息 (TNF 欄位值 0x05)。這個通訊協定只適用於訂閱,在發行 NDEF 內容時,請使用 NDEF

 

 

        簡單說明一下,Windows 開頭的玩意其實最終的結果和用 NDEF 開頭寫起來沒什麼兩樣,差別在於使用 Windows 開頭的協定時你寫程式可以少很多麻煩,因為寫程式自行處理 NDEF 中的一些標頭設定很容易搞死人,因此 NDEF 以後再講,就先拿 Windows 開頭的協定來做入門;另一個要特別提到的是有沒有帶 [ :WriteTag] 的差別在於有帶這個協定表示是要對著卡片做 Publish 使用的。

 

 

        關於Publish 的方法其實有好幾個,第一個是通用的 ProximityDevice.PublishBinaryMessage ,為什麼說它通用,因為不論什麼東西都可以用 PublishBinaryMessage 來達成,所差只是麻不麻煩的問題。這個方法有兩個多載:

(1) PublishBinaryMessage(String, IBuffer)
(2) PublishBinaryMessage(String, IBuffer, MessageTransmittedHandler)

        如果你發行完訊息沒要做什麼事可以選用第一個多載,但通常世界都沒這麼簡單,所以第二的多載可能比較容易被用上,以下是其參數說明:

 

messageType

類型: String [JavaScript] | System.String [.NET] | Platform::String [C++]

要傳遞給訂閱者之訊息的型別。

 

message

類型: IBuffer

要傳遞給訂閱者的二進位訊息資料。

 

messageTransmittedHandler

類型: MessageTransmittedHandler

已傳送訊息時所要呼叫的處理常式。

 

 

        請注意 messageTransmittedHandler 委派所指向的委派函式將會執行在非目前 UI 的執行緒當中,也就是說當你要在此函式內部變更 UI 物件時需要注意到跨執行緒存取的問題。如果單純只是要 Publish 文字資料可以改用 ProximityDevice.PublishMessage ;Uri 資料可以使用 ProximityDevice.PublishUriMessage

 

 

        接下來的範例是在 Window Phone 上採用 PublishBinaryMessage 來發送一個自訂的訊息


	public partial class MainPage : PhoneApplicationPage
	{
		private string messagetype;
		private ProximityDevice proximitydevice;
		private long publishMessageID = -1;

		public MainPage()
		{
			InitializeComponent();
			proximitydevice = ProximityDevice.GetDefault();
		}

		private void Button_Click_1(object sender, RoutedEventArgs e)
		{
			msgtxt.Text = String.Empty;
			if (proximitydevice != null)
			{
				messagetype = "Windows.BillSample";
				var dataWriter = new Windows.Storage.Streams.DataWriter();
				dataWriter.WriteString("Hello NFC");
				publishMessageID = proximitydevice.PublishBinaryMessage(messagetype,
					dataWriter.DetachBuffer(), MessagePublishedToCard);
			}
		}

		private void MessagePublishedToCard(ProximityDevice sender, long messageID)
		{
			proximitydevice.StopPublishingMessage(publishMessageID);
			publishMessageID = -1;
			Dispatcher.BeginInvoke(new Action(() => WriteMessage("寫入成功!")));
		}

		private void Button_Click_2(object sender, RoutedEventArgs e)
		{
			msgtxt.Text = String.Empty;
			if (proximitydevice != null)
			{
				messagetype = "Windows:WriteTag.BillSample";
				var dataWriter = new Windows.Storage.Streams.DataWriter();
				dataWriter.WriteString("Hello NFC");
				publishMessageID = proximitydevice.PublishBinaryMessage(messagetype,
					dataWriter.DetachBuffer(), MessagePublishedToCard);
			}
		}


		private void WriteMessage(string message)
		{ msgtxt.Text = message; }

		private void Button_Click_3(object sender, RoutedEventArgs e)
		{
			msgtxt.Text = string.Empty;
			if (proximitydevice != null)
			{
				messagetype = "WindowsUri";
				var dataWriter = new Windows.Storage.Streams.DataWriter();
				dataWriter.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
				dataWriter.WriteString("http://www.google.com");
				publishMessageID = proximitydevice.PublishBinaryMessage(messagetype,
					dataWriter.DetachBuffer(), MessagePublishedToCard);
			}
		}


		private void Button_Click_5(object sender, RoutedEventArgs e)
		{
			if (proximitydevice != null)
			{
				publishMessageID = proximitydevice.PublishUriMessage(new Uri("http://www.MSN.com")
					, MessagePublishedToCard);
			}
		}

	}

 

        (1) 在 Button_Click_1 方法中,定義了一個自定的 Message Type “Windows.BillSample”,接著使用 DataWriter 類別中的 DataWriter.WriteString 方法將字串寫入到輸出資料流中;由於在 PublishBinaryMessage 中所使用的資料參數型別為 IBuffer ,所以會呼叫 DataWriter.DetachBuffer 方法輸出 IBuffer,當你呼叫 DetachBuffer 後,DataWriter 中的資料流將會被清空;接著我們在發行完畢後呼叫一個處理常式 MessagePublishedToCard ,這個函式只做了很簡單的處理,第一個是呼叫 StopPublishingMessage 方法來停止繼續發行 (如果你是要按一次 Button 就不斷地發行,就不需要呼叫這個函式),接著使用Dispatcher.BeginInvoke 呼叫另一個函式 WriteMessage 將『寫入成功』輸出到畫面上的 TextBlock 上。

 

 

 

        (2) Button_Click_2 其實內容和前述 Button_Click_1 差不多,只有在 Message type 上做了修改 – “Windows:WriteTag.BillSample”,所以這個方法所要寫入的對象是 Tag。

 

 

 

        (3) Button_Click_3 和 Button_Click_5 是示範如何發行 Uri,在 Button_Click_3 中使用 PublishBinaryMessage 方法來寫入,所以程式碼會需較多自行處理部份,需要注意的是 dataWriter.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE 這一行,因為寫入 Uri 需使用 UTF16LE 編碼。然而,如果你如同 Button_Click_5 方法中使用的是 PublishUriMessage 的話,程式碼看起來就輕鬆多了。

 

 

 

        下一篇將會使用 Windows Store App 的程式碼來示範如何訂閱訊息。