Windows Phone Push Notification 原理与Demo

在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)



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

除了整個傳送機制之外,依據適用情境的不同,Notification還區分為三種:RAW notificationTile NotificationToast Notification下表是小弟初步整理出來的比較。

 Toast NotificationTile NotificationRAW Notification
呈現方式直接彈出並覆蓋在目前使用者的視窗上面更新在快捷區(quick launch area)中 狀態磚(tile) 的狀態由程式接收、處理、並自訂回應方式。
有效期間  僅限程式在foreground
通知範圍OS-wideOS-wideApplication-wide
適用情境及時告知狀態更新資料更新

 

 說了這麼多,不來點程式碼似乎很沒有「感覺」。那麼,我們就以Toast Notification為例,來感受一下Push Notification的威力吧!

 

這邊我只列出幾個較重要的重點步驟,這個程式碼是我從網路上的範例程式改良並精簡過的,希望能盡量不讓不相干的程式碼干擾看倌們的理解。至於完整可以RUN的版本,請下載Demo檔回去測試喔,謝謝。

WP7的部分,在註冊頻道時,需要下列的步驟

 

             //Step1:建立頻道 
            HttpNotificationChannel httpChannel =  null
 
             string channelName =  "ToastNotificationTest"
 
             //先尋找是否已有此頻道 
             //若有,先將之關閉 
            httpChannel = HttpNotificationChannel.Find(channelName);      
             if (httpChannel !=  null
            
                httpChannel.Close(); 
                httpChannel.Dispose(); 
            }
 
 
            httpChannel =  new HttpNotificationChannel(channelName); 
 
             //Step2:訂閱該頻道的event 
 
             //當頻道成功開啟會觸發該事件 
            httpChannel.ChannelUriUpdated +=  new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated); 
             //當例外發生會觸發該事件 
            httpChannel.ErrorOccurred +=  new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ErrorOccurred); 
             //當收到toast notification會觸發該事件 
            httpChannel.ShellToastNotificationReceived +=  new EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived); 
 
             //Step4:開啟頻道 
            httpChannel.Open(); 
 
             //Step5:指定該頻道接聽Toast notification 
             httpChannel.BindToShellToast(); 

 另外,還需補上event handler來處理channel的event

         //Step3:撰寫對應的event handler 
         void httpChannel_ChannelUriUpdated( object sender, NotificationChannelUriEventArgs e) 
        
            //當頻道開啟成功後,會回傳一個ChannelUri 
            //我們將這個Uri貼到ToastNotificationSender程式的Uri Textbox中 
            Debug.WriteLine(e.ChannelUri); 
        }
 
 
         void httpChannel_ErrorOccurred( object sender, NotificationChannelErrorEventArgs e) 
        
            //發生錯誤:顯示錯誤訊息 
            Dispatcher.BeginInvoke(() => 
            
                txtMessage.Text = e.Message; 
            }
); 
        }
 
 
         void httpChannel_ShellToastNotificationReceived( object sender, NotificationEventArgs e) 
        
            //收到訊息:將之顯示在畫面上 
            foreach (var key in e.Collection.Keys) 
            
                string msg = e.Collection[key]; 
 
                Dispatcher.BeginInvoke(() => 
                
                    txtMessage.Text += key + ": " + msg + "\r\n"
                }
); 
            }
 
 
        }

Server端,發送push notification時,含下列步驟

 我們用一個WinForm程式模擬發出通知訊息,需注意一點:此處的Uri值是由上面程式在頻道開啟成功後回傳的,可由Debug outpupt視窗中複製過來

 

             //簡易防呆檢查 
             if (txtUri.Text ==  string.Empty) 
            
                MessageBox.Show("Uri不能為空"); 
                return
            }
 
             if (txtText1.Text ==  string.Empty || txtText2.Text ==  string.Empty) 
            
                MessageBox.Show("Text1或Text2不能為空"); 
                return
            }
 
 
             //準備POST Message 
            HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(txtUri.Text); 
            sendNotificationRequest.Method = WebRequestMethods.Http.Post; 
            sendNotificationRequest.ContentType =  "text/xml; charset=utf-8"
 
             //以下為建構出符合Toast Norification格式的Message 
             //若為要改為RAW或tile Notification格式 
             //可參考 http://msdn.microsoft.com/en-us/library/ff402545(VS.92).aspx 
 
             //準備HttpHeader 
            sendNotificationRequest.Headers =  new WebHeaderCollection(); 
            sendNotificationRequest.Headers[ "X-MessageID"] = Guid.NewGuid().ToString(); 
            sendNotificationRequest.Headers.Add( "X-WindowsPhone-Target""toast"); 
            sendNotificationRequest.Headers.Add( "X-NotificationClass""2"); 
 
             //準備XML內容 
             //此處我們採用寫死的作法,只為了簡化不必要的步驟以幫助理解,實務上不建議採用 
             //注意:XML格式有可能變動,若有format error的訊息請依循最新版本 
             //http://social.msdn.microsoft.com/Forums/en/windowsphone7series/thread/64863ad2-895c-4179-8367-b846ad816fc5 
 
             string ToastPushXML =  "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + 
                                     "<wp:Notification xmlns:wp=\"WPNotification\">" + 
                                       "<wp:Toast>" + 
                                         "<wp:Text1>{0}</wp:Text1>" + 
                                         "<wp:Text2>{1}</wp:Text2>" + 
                                       "</wp:Toast>" + 
                                     "</wp:Notification>"
             
             //將XML序列化 
             string str =  string.Format(ToastPushXML, txtText1.Text, txtText2.Text); 
             byte[] strBytes =  new UTF8Encoding().GetBytes(str); 
            sendNotificationRequest.ContentLength = strBytes.Length; 

             using  (Stream requestStream = sendNotificationRequest.GetRequestStream()) 
            
                requestStream.Write(strBytes, 0, strBytes.Length); 
            }
 
 
             //送出內容並取得HttpResponse (在正式系統上建議改為非同步呼叫) 
            HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse(); 
             string notificationStatus = response.Headers[ "X-NotificationStatus"]; 
             string deviceConnectionStatus = response.Headers[ "X-DeviceConnectionStatus"];    
            lblMessage.Text =  "狀態: " + notificationStatus +  "連線: " + deviceConnectionStatus;

 

我們來模擬一下各種情境,當我們正在用必應的時候....

恩,我會記得帶傘 ...

糟糕,上面扛了50個VM...

 

先吃完才有力氣修雲端一號...

 買十次總有一次可以「恭喜會員大賺」... =_=

這個應用是我覺得最實用的一個...

(真的是隨便想就好幾個應用呢 XD )

結語:
其實不論RAW、Tile、Toast notification的實作方式都大同小異,大致上只是註冊接聽的類型與訊息格式不同而已。其中RAW的部分還可以自定訊息格式,由程式來實作parse的部分,如此一來,如果能善用這個機制,可以想像的變化彈性還不小呢!在Windows Phone 7 Developer Training Kit上有一個較完整的範例,他的Server部分完整實作一個註冊機制,當有事件要通知時可以找到有誰訂閱,並依序發出訊息。步驟大約有一兩百步,如果想挑戰的朋友歡迎下載來玩玩 (好,我先承認,我做不到第40步就放棄了 )

P.S 1:本文因為倉促成文,又因Beta產品,網路資源有限,有些資料可能會有錯的部分,還會陸續更正,也請各位先進能不吝指導,甘溫~
P.S 2:Demo程式碼是我從範例程式改良並精簡來的,有加上註解,並去掉很多無關架構的部分,希望能幫助大家快速理解整個運作方式。

Demo程式碼下載:
ToastNotificationDemo_100909.zip

參考文獻:
Using Push Notifications
Windows Phone 7 Developer Training Kit
Implementing Push Notifications in Windows Phone 7 - UPDATED (April CTP)
Using Push Notification from Your Windows Phone Application
Windows Phone 7 Push Notifications
Push Notifications for Tiles with Windows Phone 7

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值