Windows Phone 7 - Push Notification机制初探

在Windows phone 7即将问世之际,笔者想要跟大家分享一个在开发上很好用的机制:Push notification。以往,我们习惯使用Web Service或WCF service来让本地程式与远端伺服器沟通,随时随地,随心所欲的存取资料,always connect!但这个方式到了新的云端应用上,却出现了新的挑战。

首先,因为系统资源、效能与电力等考量,不支援3rd party application 的Multitasking。这也意味着,除了原本内建的「First Party」程式之外,我们在上面开发的程式,当不在foreground时,便会进入suspended(dehydrated)状态,没有办法持续运作。那么,远端如果有资讯的更新,本地端不就没有办法及时反映了吗?这时Push notification就派上用场啦。只要善用这个机制,即使程式进入suspended状态,远端的讯息还是可以顺利的传送到本地端,被Windows Phone接收与处理,即时的反映给使用者。另一方面,在foreground的程式也不需要持续的Pooling(论询),就可以维持在最新状态。

它是如何做到的呢?请参照下图一:
首先,你的程式要先向MPN Service注册(建立)一个WP与MPN之间的channel (步骤1),注册成功之后,MPN Service会回传一个Uri,你拿着这个Uri交给你的云端服务之后(步骤2),接下来它就可以透过这个Uri对你传送Push Notification﹝其实是先送到MPN Server,再由MPN Service转送给你的WP﹞ (步骤3与4)。

1.png

图一: Push Notification 运作模式(引用自The Windows Phone Developer Blog)


除了 ​​整个传送机制之外,依据适用情境的不同,Notification还区分为三种:RAW notification 、Tile Notification 、Toast Notification 。下表是小弟初步整理出来的比较。

Toast Notification Tile Notification RAW Notification
呈现方式 直接弹出并覆盖在目前使用者的视窗上面 更新在快捷区(quick launch area)中状态砖(tile) 的状态 由程式接收、处理、并自订回应方式。
有效期间     仅限程式在foreground
通知范围 OS-wide OS-wide Application-wide
适用情境 及时告知 状态更新 资料更新

说了这么多,不来点程式码似乎很没有「感觉」。那么,我们就以Toast Notification为例,来感受一下Push Notification的威力吧! 这边我只列出几个较重要的重点步骤,这个程式码是我从网路上的范例程式改良并精简过的,希望能尽量不让不相干的程式码干扰看倌们的理解。至于完整可以RUN的版本,请下载Demo档回去测试喔,谢谢。WP7的部分,在注册频道时,需要下列的步骤
  1. //Step1:建立频道 
  2.             HttpNotificationChannel httpChannel = null; 

  3.             string channelName = "ToastNotificationTest"; 

  4.             //先寻找是否已有此频道 
  5.             //若有,先将之关闭 
  6.             httpChannel = HttpNotificationChannel.Find(channelName);      
  7.             if (httpChannel != null) 
  8.             { 
  9.                 httpChannel.Close(); 
  10.                 httpChannel.Dispose(); 
  11.             } 

  12.             httpChannel = new HttpNotificationChannel(channelName); 

  13.             //Step2:订阅该频道的event 

  14.             //当频道成功开启会触发该事件 
  15.             httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated); 
  16.             //当例外发生会触发该事件 
  17.             httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ErrorOccurred); 
  18.             //当收到toast notification会触发该事件 
  19.             httpChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived);

  20.             //Step4:开启频道 
  21.             httpChannel.Open(); 

  22.             //Step5:指定该频道接听Toast notification 
  23.             httpChannel.BindToShellToast();
复制代码

另外,还需补上event handler来处理channel的event
  1. //Step3:撰写对应的event handler 
  2.         void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e) 
  3.         { 
  4.             //当频道开启成功后,会回传一个ChannelUri 
  5.             //我们将这个Uri贴到ToastNotificationSender程式的Uri Textbox中 
  6.             Debug.WriteLine(e.ChannelUri); 
  7.         } 

  8.         void httpChannel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e) 
  9.         { 
  10.             //发生错误:显示错误讯息 
  11.             Dispatcher.BeginInvoke(() => 
  12.             { 
  13.                 txtMessage.Text = e.Message; 
  14.             }); 
  15.         } 

  16.         void httpChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e) 
  17.         { 
  18.             //收到讯息:将之显示在画面上 
  19.             foreach (var key in e.Collection.Keys) 
  20.             { 
  21.                 string msg = e.Collection[key]; 

  22.                 Dispatcher.BeginInvoke(() => 
  23.                 { 
  24.                     txtMessage.Text += key + ": " + msg + "\r\n"; 
  25.                 }); 
  26.             } 

  27.         }
复制代码

Server端,发送push notification时,含下列步骤 我们用一个WinForm程式模拟发出通知讯息,需注意一点:此处的Uri值是由上面程式在频道开启成功后回传的,可由Debug outpupt视窗中复制过来
  1. //简易防呆检查 
  2.             if (txtUri.Text == string.Empty) 
  3.             { 
  4.                 MessageBox.Show("Uri不能为空"); 
  5.                 return; 
  6.             } 
  7.             if (txtText1.Text == string.Empty || txtText2.Text == string.Empty) 
  8.             { 
  9.                 MessageBox.Show("Text1或Text2不能为空"); 
  10.                 return; 
  11.             } 

  12.             //准备POST Message 
  13.             HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(txtUri.Text); 
  14.             sendNotificationRequest.Method = WebRequestMethods.Http.Post; 
  15.             sendNotificationRequest.ContentType = "text/xml; charset=utf-8"; 

  16.             //以下为建构出符合Toast Norification格式的Message 
  17.             //若为要改为RAW或tile Notification格式 
  18.             //可参考 http://msdn.microsoft.com/en-us/library/ff402545(VS.92).aspx 

  19.             //准备HttpHeader 
  20.             sendNotificationRequest.Headers = new WebHeaderCollection(); 
  21.             sendNotificationRequest.Headers["X-MessageID"] = Guid.NewGuid().ToString(); 
  22.             sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast"); 
  23.             sendNotificationRequest.Headers.Add("X-NotificationClass", "2"); 

  24.             //准备XML内容 
  25.             //此处我们採用写死的作法,只为了简化不必要的步骤以帮助理解,实务上不建议採用 
  26.             //注意:XML格式有可能变动,若有format error的讯息请依循最新版本 
  27.             //http://social.msdn.microsoft.com/Forums/en/windowsphone7series/thread/64863ad2-895c-4179-8367-b846ad816fc5 

  28.             string ToastPushXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + 
  29.                                     "<wp:Notification xmlns:wp=\"WPNotification\">" + 
  30.                                       "<wp:Toast>" + 
  31.                                         "<wp:Text1>{0}</wp:Text1>" + 
  32.                                         "<wp:Text2>{1}</wp:Text2>" + 
  33.                                       "</wp:Toast>" + 
  34.                                     "</wp:Notification>"; 
  35.              
  36.             //将XML序列化 
  37.             string str = string.Format(ToastPushXML, txtText1.Text, txtText2.Text); 
  38.             byte[] strBytes = new UTF8Encoding().GetBytes(str); 
  39.             sendNotificationRequest.ContentLength = strBytes.Length; 

  40.             using (Stream requestStream = sendNotificationRequest.GetRequestStream()) 
  41.             { 
  42.                 requestStream.Write(strBytes, 0, strBytes.Length); 
  43.             } 

  44.             //送出内容并取得HttpResponse (在正式系统上建议改为非同步呼叫) 
  45.             HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse(); 
  46.             string notificationStatus = response.Headers["X-NotificationStatus"]; 
  47.             string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];    
  48.             lblMessage.Text = "状态: " + notificationStatus + "连线: " + deviceConnectionStatus;
复制代码
我们来模拟一下各种情境,当我们正在用必应的时候.... 2.png 结语:
其实不论RAW、Tile、Toast notification的实作方式都大同小异,大致上只是注册接听的类型与讯息格式不同而已。其中RAW的部分还可以自定讯息格式,由程式来实作parse的部分,如此一来,如果能善用这个机制,可以想像的变化弹性还不小呢!在Windows Phone 7 Developer Training Kit上有一个较完整的范例,他的Server部分完整实作一个注册机制,当有事件要通知时可以找到有谁订阅,并依序发出讯息。步骤大约有一两百步,如果想挑战的朋友欢迎下载来玩玩(好,我先承认,我做不到第40步就放弃了)PS 1:本文因为仓促成文,又因Beta产品,网路资源有限,有些资料可能会有错的部分,还会陆续更正,也请各位先进能不吝指导,甘温~ 
PS 2:Demo程式码是我从范例程式改良并精简来的,有加上注解,并去掉很多无关架构的部分,希望能帮助大家快速理解整个运作方式。Demo程式码下载:
游客,如果您要查看本帖隐藏内容请 回复



3.png (42.46 KB, 下载次数: 5)

3.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值