跟林永堅老師說wp7
代碼下載
概述:
- 為什麼使用推送服務
- 推播通知服務的原理
- 使用規範
- 訊息類型(Raw Notification,Toast Notification,tile Notification)
- 定時更新Tile
首先為什麼使用推送服務?Windows Phone執行模型決定了只有一個第三方的應用可以在前台運行,所以第三方的應用就不能在後台不斷的往cloud拉資料,微軟提供推播通知服務給第三方應用取得更新通知的訊息,伺服器主動發起通訊,可以減少電池的消耗,
那麼推播通知服務的過程和原理是怎樣的 呢:
- WP裝置到MPNS註冊PN服務,並得到唯一的URI,
- Wp裝置吧服務URI傳遞給Cloud服務,並註冊
- 當有更新訊息發生時,Cloud往MPNS發送通知
- MPNS把訊息把更新通知發送到WP裝置上
使用規範:
wp7.0版本只支援最多15個推播通知服務,所以最好詢問使用者是否使用推播通知服務,為使用者提供取消訂閱的選項
訊息類型:
- Raw Notification:可以發送任何格式的資料,應用程式可以根據需要加工資料,只有在程式啟動並執行時候才接收訊息
- Toast Notification:發送的資料指定為XML,如果程式正在運行,訊息發送到應用中,如果程式沒有在運行,彈出Toast 訊息框顯示訊息,
- Tile Notification:發送的資料指定為XML,程式運行時不會接收訊息,只有程式被Pin To Start時,更新資料才會發送到Start Screen的 Tile裡面,包含三個屬性:背景,標題,數目,每個屬性都有固定的格式與位置,可以使用其中的屬性,不一定三個一起使用,
Xaml.cs代碼:
using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Windows;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Shapes;using Microsoft.Phone.Controls;using Microsoft.Phone.Notification;using System.Diagnostics;using System.IO;using System.Text;namespace PushNotifications{ public partial class MainPage : PhoneApplicationPage { HttpNotificationChannel httpchanel; string channelname = "channel1"; // Constructor public MainPage() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { httpchanel = HttpNotificationChannel.Find(channelname); if (httpchanel != null) { httpchanel.Close(); httpchanel.Dispose(); } httpchanel = new HttpNotificationChannel(channelname, "NotificationService"); httpchanel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpchanel_ChannelUriUpdated); httpchanel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpchanel_ErrorOccurred); httpchanel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(httpchanel_HttpNotificationReceived); //程式運行時處理toast; httpchanel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(httpchanel_ShellToastNotificationReceived); httpchanel.Open(); //程式不在運行時處理toast; httpchanel.BindToShellToast(); //程式不在運行時處理toast; httpchanel.BindToShellTile(); } void httpchanel_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())); } void httpchanel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e) { using(var Reader=new StreamReader(e.Notification.Body)) { string msg = Reader.ReadToEnd(); Dispatcher.BeginInvoke(() => { MessageBox.Show(msg); }); } } void httpchanel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e) { Dispatcher.BeginInvoke(()=>{ MessageBox.Show(e.Message); }); } void httpchanel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e) { Debug.WriteLine("ChannelUri{0}", e.ChannelUri); } }}
xaml代碼:
<phone:PhoneApplicationPage x:Class="PushNotifications.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True"> <!--LayoutRoot is the root grid where all page content is placed--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel contains the name of the application and page title--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="示範程式" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="推播通知服務" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Button Content="串連" Height="72" HorizontalAlignment="Left" Margin="44,50,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" /> <TextBlock Height="124" HorizontalAlignment="Left" Margin="44,160,0,0" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top" Width="269" /> </Grid> </Grid> <!--Sample code showing usage of ApplicationBar--> <!--<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/> <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/> <shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="MenuItem 1"/> <shell:ApplicationBarMenuItem Text="MenuItem 2"/> </shell:ApplicationBar.MenuItems> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>--></phone:PhoneApplicationPage>
雲端程式處理代碼:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Net;using System.IO;namespace CloudServer{ public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void SendRawMsg(byte[] bytes) { HttpWebRequest sendrequest = (HttpWebRequest)WebRequest.Create(textBox1.Text); sendrequest.Method = WebRequestMethods.Http.Post; sendrequest.Headers["X-MessageID"] = Guid.NewGuid().ToString(); sendrequest.ContentType = "text/xml;charset=utf-8"; sendrequest.Headers.Add("X-NotificationClass", "3"); sendrequest.ContentLength = bytes.Length; byte[] notificationmsg = bytes; using (Stream requeststream = sendrequest.GetRequestStream()) { requeststream.Write(notificationmsg, 0, notificationmsg.Length); } HttpWebResponse response = (HttpWebResponse)sendrequest.GetResponse(); string msgstatus = response.Headers["X-NotificationStatus"]; string channelstatus = response.Headers["X-SubscriptionStatus"]; string devicestatus = response.Headers["X-DeviceConnectionStatus"]; label6.Text = string.Format("訊息狀態:{0},管道狀態:{1},裝置串連狀態:{2}", msgstatus, channelstatus, devicestatus); } private void button1_Click(object sender, EventArgs e) { string msg = string.Format("訊息類型:{0}{1}{2},{3}度", comboBox1.Text, comboBox2.Text, comboBox3.Text, textBox5.Text); string type = comboBox1.Text.ToUpper(); MessageBox.Show(type); if (type == "RAW") { byte[] strbytes = new UTF8Encoding().GetBytes(msg); SendRawMsg(strbytes); } if (type == "TOAST") { string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<wp:Notification xmlns:wp=\"WPNotification\">" + "<wp:Toast>" + "<wp:Text1>天氣更新</wp:Text1>" + "<wp:Text2>" + msg + "</wp:Text2>" + "</wp:Toast> " + "</wp:Notification>"; byte[] strbytes = new UTF8Encoding().GetBytes(toastMessage); SendToastMsg(strbytes); } if (type == "TILE") { string tileMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<wp:Notification xmlns:wp=\"WPNotification\">" + "<wp:Tile>" + "<wp:BackgroundImage>Red.jpg</wp:BackgroundImage>" + "<wp:Title>" + msg + "</wp:Title>" + "</wp:Tile> " + "</wp:Notification>"; byte[] strbytes = new UTF8Encoding().GetBytes(tileMessage); SendTileMsg(strbytes); } } private void SendTileMsg(byte[] tile) { 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 URIs coming from the web client and maintain a list of URIs to send // notifications out to. string subscriptionUri = textBox1.Text; HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri); // Create an HTTPWebRequest that posts the toast notification to the Microsoft Push Notification Service. // HTTP POST is the only method allowed 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["X-MessageID"] = Guid.NewGuid().ToString(); // Set the notification payload to send. byte[] notificationMessage = tile; // Set 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"); 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. label6.Text = notificationStatus + " | " + deviceConnectionStatus + " | " + notificationChannelStatus; } catch (Exception ex) { label6.Text = "Exception caught sending update: " + ex.ToString(); } } private void SendToastMsg(byte[] toastmsg) { 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 URIs coming from the web client and maintain a list of URIs to send // notifications out to. string subscriptionUri = textBox1.Text; HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri); // Create an HTTPWebRequest that posts the toast notification to the Microsoft Push Notification Service. // HTTP POST is the only method allowed 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["X-MessageID"] = Guid.NewGuid().ToString(); sendNotificationRequest.ContentType = "text/xml"; sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast"); sendNotificationRequest.Headers.Add("X-NotificationClass", "2"); // Set the web request content length. sendNotificationRequest.ContentLength = toastmsg.Length; // Set the notification payload to send. byte[] notificationMessage = toastmsg; 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. label6.Text = notificationStatus + " | " + deviceConnectionStatus + " | " + notificationChannelStatus; } catch (Exception ex) { label6.Text = "Exception caught sending update: " + ex.ToString(); } } }}
: