当Windows Azure 碰到了Windows Phone 7:推送通知服务概述
本文将介绍Windows Phone 7 上的一个新功能-Push Notification Service,一个由应用程序供货商与手机用户进行沟通的管道,服务应用程序可以利用这个功能将讯息广播给所有订阅此服务的手机使用者,如同简讯一般。
Push Notification Services 简介
Windows Phone 7(WP7)已于11 月中旬正式上市,台湾目前可以买到HTC HD7 以及HTC Mozart 两款WP7 手机,除了可以让台湾的使用者能够抢先体验WP7 之外,台湾的Windows Phone 开发人员也开始不断的将WP7 的应用程序提交到App Hub(WP7 的应用程序中心)让全球的WP7 使用者可以下载使用。WP7 挟带者全新的手机体验进入手机的市场,不论是使用者的使用经验,或是开发人员的开发经验,都是一种全新的境界。对于开发人员来说,WP7 上的应用程序开发模型和Windows Mobile 完全不同,就像是在桌上型应用程序由Windows Forms 移转到WPF(Windows Presentation Foundation)一样,WP7 的开发也由Windows Mobile 上的Smart Device Application(Windows Forms-based)转换为Silverlight Application,象征未来Windows 平台上的应用程序开发会全面移转到WPF/Silverlight,所有发展Windows 应用程序的开发人员将无法避免这个时代的洪流。
WP7 除了应用程序开发模式的不同,连通讯的功能也起了不小的变化,在以往Windows Mobile 的时代,若要实作通讯的应用,开发人员必须要自客户端到服务器,都要一手包办,其中最难的地方是服务器的部份,以及如何将讯息传到特定的Windows Mobile 手机上,Windows Mobile 本身能够识别手机本身的支持很少,要以Windows Mobile 实作异步讯息的应用的复杂度并不低,不过在现阶段云端运算技术,尤其是Windows Azure 平台的演进已成熟之际,微软特别在Windows Azure 上为WP7 架设了一个讯息的交换中心,称为Microsoft Push Notification Services(MPNS),它可以支持由云端应用程序或服务应用程序透过它将讯息传递给WP7 手机,而WP7 的通道由WP7 应用程序提交给服务应用程序,然后由服务应用程序将讯息发送给MPNS,再由MPNS 将讯息发送给WP7 手机,整体架构如下图。
透过MPNS 的讯息通知服务,WP7 上的应用程序可以很容易的由云端应用程序上获得不同的讯息,并实时更新用户接口以告知手机的使用者(例如火车或电影院的购票划位完成通知),而WP7 上的Notification Framework 让开发人员在使用MPNS 开发应用时能更加的便利。
Notification 类型
WP7 对于MPNS 上的客户端支持,可以分为三种讯息格式:纯讯息、讯息砖块以及讯息列,这三种讯息可支持不同的使用情境,而WP7本身也对这三种讯息格式有不同的用户接口的支持。
纯讯息(RAW)是一种不特别对讯息做格式上处理的数据流,云端应用程序可以向MPNS 服务发送以字节为基础的数据流,开发人员可以在数据流内写入字节的数据,WP7 操作系统本身并不会针对RAW 讯息进行用户接口的处理,也就是说WP7 的应用程序要全权处理RAW 讯息,这适合在WP7 与服务间交换数据的功能,或是想要自己控制处理讯息通知的行为与用户接口的需求。
讯息砖块(Tile)是一种会安排在WP7 首页内的Quick Launch 区域中的用户接口组件,由一个方型的用户接口组件,它可以由开发人员设定安插在首页的Quick Launch 区,并且与MPNS 连接以撷取来自服务应用程序的讯息,并且在收到讯息时更新这个方型的用户接口区,以通知用户有来自服务应用程序的讯息(例如使用不同的图片或突出的用户接口效果来通知用户),当按下该砖块时即会加载应用程序。
讯息列(Toast)则是会出现在WP7 用户接口内的一种讯息通知方式,它比较像是现在IE9 或Google Chrome 的信息列,当MPNS 有讯息进来时,会显示在WP7 手机画面的上方或下方(由WP7 以及应用程序决定)。
显示在Quick Launch 的Toast 讯
息显示在应用程序内的Toast 讯
息
发送的讯息类别,由云端应用程序决定,发送RAW 讯息时会以二进制格式来传输,而发送Tile 或Toast 讯息时,则要求要依照指定的格式来传送才行。
Push Notification 应用程序实作概观
WP7 的MPNS 服务分为三个部份,分别是客户端、服务器与讯息设定三个部份。
客户端的部份由WP7 内的Microsoft.Phone.dll 所封装的Microsoft.Phone.Notification 命名空间支持,内含下列主要对象:物件说明
HttpNotification 包装来自MPNS 的讯息内容。
HttpNotificationChannel 提供与MPNS 的双向通讯功能,对Tile 与Toast 讯息来类别提供与用户接口系结(Bind)的能力,而对Raw 讯此类别提供应用程序可与MPNS 通讯并下载Raw 讯息的
而客户端在与云端应用程序要求讯息通知机制前,必须要先向MPNS 提交通讯需求,以取得专属此手机的通讯URI,这个URI 会由WP7 应用程序提供的服务名称以及通道名称来产生,而WP7 客户端应用程序要使用HttpNotificationChannel 来处理向MPNS 要求URI 以及接收来自于MPNS 的讯息数据,下列程序代码即是使用HttpNotificationChannel 对象与MPNS 取得联系的范例:
[C#]
1.public HttpNotificationChannel myChannel;
2.public void CreatingANotificationChannel()
3.{
4. myChannel = HttpNotificationChannel.Find("MyChannel");
5.
6.if (myChannel == null)
7. {
8.// Only one notification channel name is supported per appli
cation.
9. myChannel = new HttpNotificationChannel("MyChannel","
https://www.doczj.com/doc/7711704716.html,");
10.
11. SetUpDelegates();
12.
13.// After myChannel.Open() is called, the notification
14.// channel URI will be sent to the application through the Channel
UriUpdated delegate.
15.// If your application requires a timeout for setting up a notif
ication channel,
16.// start it after the myChannel.Open() call.
17. myChannel.Open();
18. }
19.else// Found an existing notification channel.
20. {
21. SetUpDelegates();
22.
23.// The URI that the application sends to its web service.
24. Debug.WriteLine("Notification channel URI:" + myChannel
.ChannelUri.ToString());
25.
26.if (myChannel.ChannelUri == null)
27. {
28.// The notification channel URI has not been sent to the cl
ient.
29.// Wait for the ChannelUriUpdated delegate to fire.
30.// If your application requires a timeout for setting up a no
tification channel,
31.// start it here.
32. }
33. }
34.
35.// An application is expected to send its notification channel U
RI to
36.// its corresponding web service each time it launches.
37.// The notification channel URI is not guaranteed to be the sam
e as the
38.// last time the application ran.
39.if (myChannel.ChannelUri != null)
40. {
41.// SendURIToService(myChannel.ChannelUri);
42. }
43.}
44.
45.public void SetUpDelegates()
46.{
47. myChannel.ChannelUriUpdated +=
48.new EventHandler
nel_ChannelUriUpdated);
49. myChannel.HttpNotificationReceived +=
50.new EventHandler
pNotificationReceived);
51. myChannel.ShellToastNotificationReceived +=
52.new EventHandler
oastNotificationReceived);
53. myChannel.ErrorOccurred +=
54.new EventHandler
annel_ErrorOccurred);
55.}
56.
57.void myChannel_ChannelUriUpdated(object sender, Notification
ChannelUriEventArgs e)
58.{
59.// The URI that the application will send to its corresponding w
eb service.
60. Debug.WriteLine("Notification channel URI:" + e.ChannelUri.
ToString());
61.// SendURIToService(e.ChannelUri);
62.}
63.
64.void myChannel_ErrorOccurred(object sender, NotificationChan
nelErrorEventArgs e)
65.{
66.switch(e.ErrorType)
67. {
68.case ChannelErrorType.ChannelOpenFailed:
69.// ...
70.break;
71.case ChannelErrorType.MessageBadContent:
72.// ...
73.break;
74.case ChannelErrorType.NotificationRateTooHigh:
75.// ...
76.break;
77.case ChannelErrorType.PayloadFormatError:
78.// ...
79.break;
80.case ChannelErrorType.PowerLevelChanged:
81.// ...
82.break;
83. }
84.}
当HttpNotificationChannel 与MPNS 建立联机(使用Open() 方法)时,即可透过HttpNotificationChannel.ChannelUri 取得此手机唯一的URI,这组URI 会作为云端应用程序将讯息发送给手机的依据。
在服务器端的部份,云端应用程序由于要取得来自WP7 客户端提交的MPNS 讯息管道URI,故在云端程序中必须要有一个允许由
WP7 应用程序登录URI 的入口,而当云端程序取得URI 后,即可利用此URI 来发送讯息。登录URI 的作法可以有很多种,例如使用HTTP 的方式或是使用WCF 的REST 服务来发展,它只要能够接取WP7 应用程序发送的URI 即可,例如下列程序即是使用WCF 的REST 服务来实作的登录服务:
[C#]
https://www.doczj.com/doc/7711704716.html,ing System;
https://www.doczj.com/doc/7711704716.html,ing System.Collections.Generic;
https://www.doczj.com/doc/7711704716.html,ing System.Linq;
https://www.doczj.com/doc/7711704716.html,ing System.Text;
5.
6.[ServiceContract]
7.public interface IRegistrationService
8.{
9. [OperationContract, WebGet]
10.void Register(string uri);
11.
12. [OperationContract, WebGet]
13.void Unregister(string uri);
14.}
15.
https://www.doczj.com/doc/7711704716.html,space WP7Azure.Service
17.{
18.public class RegistrationService : IRegistrationService
19. {
20.public static event EventHandler
Subscribed;
21.
22.private static List
23.private static object obj = new object();
24.
25.public void Register(string uri)
26. {
27. Uri channelUri = new Uri(uri, UriKind.Absolute);
28. Subscribe(channelUri);
29. }
30.
31.public void Unregister(string uri)
32. {
33. Uri channelUri = new Uri(uri, UriKind.Absolute);
34. Unsubscribe(channelUri);
35. }
36.
37. #region Subscription/Unsubscribing logic
38.private void Subscribe(Uri channelUri)
39. {
40.lock (obj)
41. {
42.if (!subscribers.Exists((u) => u == channelUri))
43. {
44. subscribers.Add(channelUri);
46. }
47. OnSubscribed(channelUri, true);
48. }
49.
50.public static void Unsubscribe(Uri channelUri)
51. {
52.lock (obj)
53. {
54. subscribers.Remove(channelUri);
55. }
56. OnSubscribed(channelUri, false);
57. }
58. #endregion
59.
60. #region Helper private functionality
61.private static void OnSubscribed(Uri channelUri, bool isAc
tive)
62. {
63. EventHandler
bed;
64.if (handler != null)
66. handler(null,
67.new SubscriptionEventArgs(channelUri, isActive));
68. }
69. }
70. #endregion
71.
72. #region Internal SubscriptionEventArgs class definition
73.public class SubscriptionEventArgs : EventArgs
74. {
75.public SubscriptionEventArgs(Uri channelUri, bool isAct
ive)
76. {
77.this.ChannelUri = channelUri;
78.this.IsActive = isActive;
79. }
80.
81.public Uri ChannelUri { get; private set; }
82.public bool IsActive { get; private set; }
83. }
84. #endregion
85.
86. #region Helper public functionality
87.public static List
88. {
89.return subscribers;
90. }
91. #endregion
92. }
93.}
当云端应用程序获取WP7 的URI 后,就可以视需求来提交讯息给
WP7 客户端,MPNS 只接受HTTP 通讯协议的讯息,我们前面有
提及讯息有Raw、Tile 和Toast 三种格式,这可以透过设定HTTP
标头的值来设定(如表):
HTTP 标头规格说明
MessageID "X-MessageID"":"1*MessageIDValue
MessageIDValue = STRING (uuid)
//For example:
X-MessageID:
头,会拒绝讯送。
NotificationClass "X-NotificationClass"":"1*NotificationClassValue
NotificationClassValue = DIGIT
//For example:
X-NotificationClass:1 此处可设定传送讯息给客户端程序周期,会因讯同而有所不
Notification Type "X-WindowsPhone-Target"":"1*NotificationTypeValue NotificationTypeValue = STRING
//For example:
X-WindowsPhone-Target:toast
通知讯息的
设定raw、
toast 三种值
CallbackURI "X-CallbackURI"":"1*CallbackURIValue
CallbackURIValue = STRING (URI)
//For example:
X-CallbackURI:
表格中所提的Notification Class 的批次周期,会依讯息类型的不同决定:
物件说明
Raw 3: 立即发送讯息。
13: 在450 秒内发送。
23: 在900 秒内发送。
Tile 1: 立即发送讯息。
11: 在450 秒内发送。
21: 在900 秒内发送。
Toast 2: 立即发送讯息。
12: 在450 秒内发送。
22: 在900 秒内发送。
接下来就是决定发送讯息的数据了:
讯息类
格式
型
Raw 直接将数据转换成二进制数据的字节数组,写入HTTP Request Stream 即Tile 由XML 字段来决定,它的格式是:
各字段相对于WP7 消息框的位置如下:
其中,background image 有一个限制,就是必须小于80KB,且在15 秒内
毕。
Toast 由XML 字段来决定,它的格式是:
准备完讯息内容后,即可正式提交讯息给MPNS 服务,MPNS 要求使用HTTP 通讯,因此使用HttpWebRequest 就可以达成这个任务:[C#]
1.//Create and initialize the request object
2.HttpWebRequest request = (HttpWebRequest)WebRequest.Creat
e(channelUri);
3.request.Method = WebRequestMethods.Http.Post;
4.request.ContentType = "text/xml; charset=utf-8";
5.request.ContentLength = payload.Length;
6.request.Headers[MESSAGE_ID_HEADER] = Guid.NewGuid().
ToString();