Push Notification并不是Windows Phone 7.1的新功能,但是之前的文章里对这部分都缺少详细的分析,所以姑且就把Push Notification放到这部分里吧。

很多iOS开发者都将WP7里的Push Notification说成抄袭iOS的产物,孰不知,微软才是Push Notification技术的先行者,Windows Mobile时代的Push Mail技术可以说是独步天下,连Symbian也要授权使用相关的技术。

Push Notification的技术为什么越来越重要,其实这跟移动设备的特点紧密相关,移动设备网络的不稳定性,决定了以Socket为代表的强连接方式并不适用,所以大家更多选择HTTP协议作为主要的通讯方式。但是HTTP的特点是,设备找服务器很容易,通过URL就可以了,但服务器找设备就难上加难了,因为设备会随时切换移动网络,IP地址之类的经常性失效。当然设备端轮询的方式可以解决这个问题,但移动设备的电源、网络都是稀缺资源。所以,OS级别的Push Notification技术就变成了一种珍贵的战略资源,而且,在封闭式的操作系统中,只有OS厂商提供的Push Notification才能够获得最好的效率。

Push Notification简介

目前,Windows Phone支持三种Push Notification方式:Toast Notifications、Tile Notifications和Raw Notifications,我不想翻译成中文名字了,因为“吐司”之类的翻译无法帮助理解。

Toast Notifications,当我们的程序没有运行时,我们希望有一种形式可以通知用户,并且让用户调用对应的应用,就像收到SMS时,调用Messaging程序一样。运行效果如下图,当用户点击Toast时,可以调用对应的程序。

Tile Notifications,用于更新启动界面上的Tile,可以通过Tile Notifications设置Tile的背景图片、Count和Title等属性,各属性显示位置如下图所示。我们可以通过该技术来提示用户,我们的应用有新的事件发生,比如SNS上有多少新的回复等。

Raw Notifications,最简单,当你的应用程序运行时,可以接收Raw Notification信息,如果程序没有运行,则接受不到Raw Notification。

接下来,我们会以Toast Notifications为例详细介绍,Push Notification的原理和使用方法。

首先,还是引用这张广为流传的图示吧,确实是目前能够找到的最详细的示意图了。下面是步骤叙述:

1-3,应用通过调用HttpNotificationChannel接口,向Microsoft Push Notification Service (MPNS)请求一个URI,这个URI会在服务器端发送Notification消息时,作为验证发送目标的唯一标识;这样做的一个好处是,当同一个设备上有多个应用在监听Notification时,不会相互干扰。

4. 应用会将获得的URI传到自己的服务器上,这个URI会存储在自己的服务器上,用于发送Push Notification时调用。URI的传递和存储方式,由开发者自己决定,不过要保证传输过程的加密,以及存储时的安全性。接下来的实例里,传递和存储URI的方式就很有创意。

5-6. 当服务器端有信息要通知应用时,需要向MPNS发起一个Http请求,而MPNS将请求转发到相应的设备上,设备上的应用接到Push Notification的消息后,进行相应的处理。

其实过程挺简单,被MSDN文档说得神乎其神。接下来,我们挨个看一下各个类型的Notification。偷点懒,不自己写Sample Code了,请大家去下面的链接里下载。不过注意Sample Code的更新,最近有同学因为更新不及时,而影响了开发进度。

http://msdn.microsoft.com/en-us/library/ff431744(VS.92).aspx

初始化 Notifications

首先是Toast Notifications,这种Notification在实际应用中最为有效,不但可以提示用户有新的消息,而且还让用户选择是否打开当时的应用。不过比起iOS的Notifications来,Toast Notifications能够容纳的信息量实在有限。所以,开发者要适当“浓缩”需要推送的消息。

我们按照调用的顺序,依次来分析代码,首先来看客户端部分ToastNotificationClient的代码:

            /// Holds the push channel that is created or found.HttpNotificationChannel pushChannel;// The name of our push channel.string channelName = "ToastSampleChannel";InitializeComponent();// Try to find the push channel.pushChannel = HttpNotificationChannel.Find(channelName);// If the channel was not found, then create a new connection to the push service.if (pushChannel == null){pushChannel = new HttpNotificationChannel(channelName);// Register for all the events before attempting to open the channel.pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);// Register for this notification only if you need to receive the notifications while your application is running.pushChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(PushChannel_ShellToastNotificationReceived);pushChannel.Open();// Bind this new channel for toast events.pushChannel.BindToShellToast();}else{// The channel was already open, so just register for all the events.pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);// Register for this notification only if you need to receive the notifications while your application is running.pushChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(PushChannel_ShellToastNotificationReceived);// Display the URI for testing purposes. Normally, the URI would be passed back to your web service at this point.System.Diagnostics.Debug.WriteLine(pushChannel.ChannelUri.ToString());MessageBox.Show(String.Format("Channel Uri is {0}",pushChannel.ChannelUri.ToString()));}
 
 

代码比较多,我们逐步来看,首先要声明一个HttpNotificationChannel的变量,命名空间为Microsoft.Phone.Notification。接下来,声明该Notification Channel的名称,该名称是Channel的唯一标识名,不能与其他Channel重复。接下来,我们调用HttpNotificationChannel的静态方法Find来查找,当前的Channel是否存在,如果存在则返回HttpNotificationChannel对象实例,不存在则返回空。如果该程序从未在当前设备上运行过,则HttpNotificationChannel对象为空,只要运行过一次,则能够通过Find方法来获取对象实例。

先来说未创建Channel的情况,创建HttpNotificationChannel的对象,并将Channel名称作为参数传递进去。然后处理该对象的两个事件:ChannelUriUpdated和ErrorOccurred。当URI发生变化时,会触发ChannelUriUpdated事件,我们在创建Channel对象时,会触发该事件,另外一个ErrorOccurred事件,则用于错误处理。

当程序已经在运行时,默认是接收不到Toast Notification消息的,所以,我们还需要处理ShellToastNotificationReceived事件,当程序运行时,也可以接收到相应的消息。

然后调用HttpNotificationChannel的Open方法,打开该Channel,最后是BindToShellToast方法,通知Shell,该Notification的Channel已经被创建。

获取Channel后的代码与之大体类似,但我们可以通过HttpNotificationChannel的ChannelUri属性来获取Channel的URI,该URI值就是需要传递给我们自己服务器的唯一值,标记了该设备上的该Channel。

而创建Channel后,我们也可以获取URI的值,不过我们需要在ChannelUriUpdated事件处理函数里获取该URI值:

        void PushChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e){Dispatcher.BeginInvoke(() =>{// Display the new URI for testing purposes.   Normally, the URI would be passed back to your web service at this point.System.Diagnostics.Debug.WriteLine(e.ChannelUri.ToString());MessageBox.Show(String.Format("Channel Uri is {0}",e.ChannelUri.ToString()));});}

到此为止,我们已经完成了Notification Channel的创建,并且获得了唯一标识的URI。

触发Notification

接下来,我们来看Server的代码。我们自己的Server与Microsoft Push Notification Server(MPNS)之间是通过Http协议,使用HttpWebRequest来发送请求。理论上来说,我们也可以使用客户端程序向MPNS发送Notification请求。为什么要用ASP.NET来编写,最主要的原因还是在于客户端应用要将URI传递给Server端,显然基于ASP.NET的Web应用在这方面是最容易的。

不过我们的实例中,没有客户端将URI传递给服务器端的代码,而是采用了最简单的办法:由开发者直接将URI拷贝到ASP.NET的Web应用中。

接下来,我们来看Server端的代码:

            try{// Get the Uri that the Microsoft Push Notification Service returns to the Push Client when creating a notification channel.// Normally, a web service would listen for Uri's coming from the web client and maintain a list of Uri's to send// notifications out to.string subscriptionUri = TextBoxUri.Text.ToString();HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);// We will create a HTTPWebRequest that posts the toast notification to the Microsoft Push Notification Service.// HTTP POST is the only allowed method to send the notification.sendNotificationRequest.Method = "POST";// The optional custom header X-MessageID uniquely identifies a notification message. // If it is present, the // same value is returned in the notification response. It must be a string that contains a UUID.// sendNotificationRequest.Headers.Add("X-MessageID", "<UUID>");// Create the toast message.string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +"<wp:Notification xmlns:wp=\"WPNotification\">" +"<wp:Toast>" +"<wp:Text1>" + TextBoxTitle.Text.ToString() + "</wp:Text1>" +"<wp:Text2>" + TextBoxSubTitle.Text.ToString() + "</wp:Text2>" +"<wp:Param>/Page2.xaml?NavigatedFrom=Toast Notification" + TextBoxSubTitle.Text.ToString() + "</wp:Param>" +"</wp:Toast> " +"</wp:Notification>";// Sets the notification payload to send.byte[] notificationMessage = Encoding.Default.GetBytes(toastMessage);// Sets the web request content length.sendNotificationRequest.ContentLength = notificationMessage.Length;sendNotificationRequest.ContentType = "text/xml";sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");sendNotificationRequest.Headers.Add("X-NotificationClass", "2");using (Stream requestStream = sendNotificationRequest.GetRequestStream()){requestStream.Write(notificationMessage, 0, notificationMessage.Length);}// Send the notification and get the response.HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();string notificationStatus = response.Headers["X-NotificationStatus"];string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];// Display the response from the Microsoft Push Notification Service.  // Normally, error handling code would be here.  In the real world, because data connections are not always available,// notifications may need to be throttled back if the device cannot be reached.TextBoxResponse.Text = notificationStatus + " | " + deviceConnectionStatus + " | " + notificationChannelStatus;}catch (Exception ex){TextBoxResponse.Text = "Exception caught sending update: " + ex.ToString();}

Server端程序的主体是调用HttpWebRequest对象,来实现HTTP POST的方法。发送的消息以XML形式呈现,在上面代码中表现为toastMessage字符串,wp:Toast指定Notification类型,wp:Text1指定标题,wp:Text2指定子标题,wp:Param指定传递的参数。

请大家注意wp:Param,我们可以认为该参数相当于调用了客户端应用中NavigationService的Navigate方法,必须确认客户端有指定的页面可以进行跳转。当用户点击Toast Notification的提示条时,客户端应用会调用wp:Param,从而跳转到指定界面上。

我们可以通过该参数来传递一些参数,比如标识本次Notification是由哪条消息引发的。不过该参数中不应该包括敏感信息、安全信息,因为无法保证这些信息的安全。最好的办法是,传递一个ID值,当客户端访问服务器时,由服务器端判断该ID是否有效。

接下来,我们还要为HTTP 头添加自定义字段,这部分参考Sample Code就可以了。

接收Notification

当Server端代码完成后,我们回到客户端,看一下当MPNS找到设备后,如何触发我们的客户端代码。首先是Page2.xaml的OnNavigatedTo方法:

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e){base.OnNavigatedTo(e);//  If we navigated to this page// from the MainPage, the DefaultTitle parameter will be "FromMain".  If we navigated here// when the secondary Tile was tapped, the parameter will be "FromTile".textBlockFrom.Text = "Navigated here from " + this.NavigationContext.QueryString["NavigatedFrom"];}

通过NavigationContext.QueryString属性来获取自定义参数,试过传两个参数,后一个收到不到,目前成功的是传递一个参数的情况。

还有一种情况是,当客户端程序运行时,接收到Toast Notification时,该如何处理:

        void PushChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e){StringBuilder message = new StringBuilder();string relativeUri = string.Empty;message.AppendFormat("Received Toast {0}:\n", DateTime.Now.ToShortTimeString());// Parse out the information that was part of the message.foreach (string key in e.Collection.Keys){message.AppendFormat("{0}: {1}\n", key, e.Collection[key]);if (string.Compare(key,"wp:Param",System.Globalization.CultureInfo.InvariantCulture,System.Globalization.CompareOptions.IgnoreCase) == 0){relativeUri = e.Collection[key];}}// Display a dialog of all the fields in the toast.Dispatcher.BeginInvoke(() => MessageBox.Show(message.ToString()));}

该方法是HttpNotificationChannel的ShellToastNotificationReceived事件处理函数,具体内容不详细解释了。

Tile Notifications

Tile Notification是另一种Notification,主要用于在Windows Phone的首页上显示Smart Tile。很多内置的WP应用可以显示Tile上的自定义显示,比如Messaging程序里可以显示当前未读的短信有多少。Tile Notification可以帮助我们实现类似的功能,通常的一个用法是,首先发送一条Toast Notification,如果用户不理睬,可以发送一条Tile Notification,在Smart Tile上显示提示,让用户在方便时,再处理该条请求。

Tile Notification的实现代码与Toast Notification类似,所以,我们不在这里列出全部源代码了,只是列出与上面不同的部分代码。

首先来看Server部分的代码:

                // Create the tile message.string tileMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +"<wp:Notification xmlns:wp=\"WPNotification\">" +"<wp:Tile>" +"<wp:BackgroundImage>" + TextBoxBackgroundImage.Text + "</wp:BackgroundImage>" +"<wp:Count>" + TextBoxCount.Text + "</wp:Count>" +"<wp:Title>" + TextBoxTitle.Text + "</wp:Title>" +"<wp:BackBackgroundImage>" + TextBoxBackBackgroundImage.Text + "</wp:BackBackgroundImage>" +"<wp:BackTitle>" + TextBoxBackTitle.Text + "</wp:BackTitle>" +"<wp:BackContent>" + TextBoxBackContent.Text + "</wp:BackContent>" +"</wp:Tile> " +"</wp:Notification>";// Sets the notification payload to send.byte[] notificationMessage = Encoding.Default.GetBytes(tileMessage);// Sets the web request content length.sendNotificationRequest.ContentLength = notificationMessage.Length;sendNotificationRequest.ContentType = "text/xml";sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "token");sendNotificationRequest.Headers.Add("X-NotificationClass", "1");

TileMessage与ToastMessage大体类似,标签变为wp:Tile,而其中包括的数据分为两组:BackgroundImage、Title和Content,显示位置可以参考文章前面的示意图。BackgroundImage可以是本地的资源,也可以是远程的图片链接。

接下来是客户端的部分代码:

                pushChannel = new HttpNotificationChannel(channelName);// Register for all the events before attempting to open the channel.pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);pushChannel.Open();// Bind this new channel for Tile events.pushChannel.BindToShellTile();

Tile Notification无需添加任何额外的代码,在调用HttpNotificationChannel的Open方法后,还需要调用BindToShellTile的方法,通知Shell绑定该Tile Notification。

Raw Notifications

Raw Notification的用法最简单,在程序运行时,收到Raw Notification时,更新应用程序界面上的某些元素。

首先,我们来看Server端的代码:

                // Create the raw message.string rawMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +"<root>" +"<Value1>" + TextBoxValue1.Text.ToString() + "<Value1>" +"<Value2>" + TextBoxValue2.Text.ToString() + "<Value2>" +"</root>";// Sets the notification payload to send.byte[] notificationMessage = Encoding.Default.GetBytes(rawMessage);// Sets the web request content length.sendNotificationRequest.ContentLength = notificationMessage.Length;sendNotificationRequest.ContentType = "text/xml";sendNotificationRequest.Headers.Add("X-NotificationClass", "3");

RawMessage非常简单,只需要指定Value1, Value2就可以了,这些值是自定义的,然后指定HTTP头的X-NotificationgClass的值为3,代表Raw Notification。

接下来是客户端代码的片段:

pushChannel = new HttpNotificationChannel(channelName);
pushChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);
pushChannel.Open();void PushChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e){string message;using (System.IO.StreamReader reader = new System.IO.StreamReader(e.Notification.Body)){message = reader.ReadToEnd();}Dispatcher.BeginInvoke(() =>MessageBox.Show(String.Format("Received Notification {0}:\n{1}",DateTime.Now.ToShortTimeString(), message)));}

HttpNotificationChannel创建完成后,只需要调用Open方法即可,不需要绑定到Shell上。另外,还需要处理HttpNotificationChannel的HttpNotificationReceived事件处理函数,具体的代码不解释了。

写在最后

好了,到这里,我们正式将Windows Phone 7.1中的Push Notification介绍完了。为了增加用户黏性,Push Notification可能是一种非常有效的方式,但是请开发者谨慎使用,如果太多的垃圾信息影响用户体验,那是得不偿失的。另外,我们还需要为Push Notification搭建一个Web服务器,这对于普通开发人员来说,也是一个很难接受的成本。

OpenXLive杯Windows Phone游戏开发大赛

OpenXLive杯Windows Phone游戏开发大赛,是由OpenXLive联合国内知名的开发者社区:DevDiv、智机网、WPMind、Silverlight银光中国和XNA游戏世界,一起举办的针对Windows Phone游戏开发的比赛。

http://www.openxlive.net/posts/news/40

本文转自马宁博客园博客,原文链接:http://www.cnblogs.com/aawolf/archive/2011/08/02/2124442.html,如需转载请自行联系原作者

马宁的Windows Phone 7.1初体验(二)——Push Notification相关推荐

  1. 马宁的Windows Phone 7.1初体验(三)——Tile

    前边介绍Push Notification时,其实已经谈到了Tile Notification.在Windows Phone 7.1中,Smart Tile得到了极大的提高.我们不但可以控制Tile的 ...

  2. Windows Embedded Standard开发初体验(二)

    支持Silverlight的Windows Embedded Standard 好了,完成安装之后,我们就可以来做Windows Embedded Standard的第一个操作系统镜像了.在开始菜单中 ...

  3. 马宁的Windows Phone 7.1初体验——XNA与Silverlight集成

    关于Windows Phone Mango真真假假的图片.视频已经在网上流传很久了,微软发布的Windows Phone 7.1 SDK可以让我们看到一部分Mango支持的功能. 普通用户对Mango ...

  4. w ndows7中桌面图标,七仔教你学习Windows 7:桌面初体验

    [IT168 软件技巧]朋友,你还在坚守"XP"吗?你还在忍受"Vista"吗?为什么不试试Windows 7?只要你曾领略她卓越的性能.华丽的外表,就一定会为 ...

  5. Windows XP SP2之初体验

    Windows XP SP2之初体验 前奏 下载了273MB的Windows XP SP2 ,然后安装,不知道安装多久,因为中途居然睡着了:-p,半夜醒来一看,已经可以重新启动了. 重新启动后,配置了 ...

  6. Windows Embedded CE 6.0开发初体验(二)CE开发环境 收藏

    上一篇<Windows Embedded CE 6.0开发初体验>之"嵌入式开发流程": http://blog.csdn.net/aawolf/archive/200 ...

  7. Windows Embedded CE 6.0开发初体验(二)CE开发环境

    CE开发环境 下面就要让今天的主角闪亮登场了--Digi CC 9P开发板.为了感谢Digi提供的开发板,还是给他们做个广告吧:这是一家以WiFi相关产品为主营业务的公司,也生产ARM架构的CPU,所 ...

  8. 树莓派驱动6轴传感器MPU6500与SPI初体验(二)

    接前回<< 树莓派驱动6轴传感器MPU6500与SPI初体验 对于一个想做嵌入开发的外围开发者,做硬件相关的应用开发真的是老太太啃骨头.一周以后凭借一点小聪明和给力的度娘,总算在树梅派上实 ...

  9. Windows Embedded Standard开发初体验(四)

    添加文件.依赖组件.注册表 接下来我们就要进入创建组件最重要的一环了,添加文件.为什么说重要,因为这里有一个大坑,我在Windows Embedded Standard产品组施卫娟老师的指导下,花了两 ...

最新文章

  1. Java设计模式-工厂模式(3)抽象工厂模式
  2. IDEA中进行SpringBoot整合spring-ws开发webservice接口后期要修改xsd文件流程
  3. 04_pandas字符串函数;数据合并concat、merge;分组groupby;Reshaping;Pivot tables;时间处理(date_range、tz_localize等)
  4. echarts在.Net中使用实例(一) 简单的Demo
  5. avws扫描出来的漏洞怎么利用_漏洞扫描利用
  6. 微软携手 Docker 打造 CNAB,分布式应用来了!
  7. rubymine 调试 redmine
  8. HttpContext HttpRuntime
  9. VC++判断文件或文件夹是否存在(转)
  10. python sklearn 梯度下降法_Python- sklearn之梯度下降算法原理
  11. ubuntu下vscode编写cgo程序
  12. 语音转文字 mp3 text
  13. EEGLAB初步学习(1)
  14. usb计算机连接文件,学习两种方式使用USB在计算机之间传输文件!
  15. 基于node连接广工大服务器获取信息
  16. 循环结构(计算0到100之间的奇数和偶数的和)
  17. Java进阶学习-7 面向对象程序设计原则
  18. 什么是电子表格(Spreadsheet)?
  19. 时间序列中季节性和非季节_在2019年会议季节生存的19个技巧
  20. 简要介绍下tensorflow的计算图

热门文章

  1. 【Android 屏幕适配】屏幕适配基础概念 ④ ( 屏幕适配限定符 | 手机/平板电脑设备屏幕适配 )
  2. 直播源码搭建教程,教你实现举报功能
  3. 《关系营销2.0——社交网络时代的营销之道》一有效电子沟通的八大法则
  4. C#将服务器端excel文件下载到客户端的简单方法
  5. web前端学习笔记之Node.js
  6. led护眼灯真的护眼吗?推荐双十一可闭眼盲入的LED灯
  7. 比超越更难的是稳住,安踏何以超耐克?
  8. 计算机网络实训怎样制作网线,计算机网络实训—双绞线的制作教学反思
  9. 初学编程两年多学习的总结!
  10. 如何获取第三方小程序的AppID和path