概述
Windows Phone 中的Microsoft Push Notification Service向第三方开发人员提供了一个弹性,专注,而且持续的渠道,使得开发人员可以从web service向移动应用程序发送信息和更新。
过去移动应用程序需要经常主动去调查其相应的Web服务,以了解是否有任何等待处理的通知。这样做虽然有效,但是会导致手机的无线设备频繁打开,从而对电池续航时间带来负面影响. 使用推送通知的方式取代主动调查,web service能够提醒应用程序获取所需要的重要更新。
图 1 推送 Notifications
当一个Web service有信息要发送到应用程序,它先发送一个通知到Push Notification Service,该服务随后将通知路由到应用程序。根据推送通知的格式和装载量,信息作为原始数据传递到应用程序,应用程序的标题明显地更新或显示一个Toast通知。然后如果需要的话应用程序可以使用自己的协议联系web service以获取更新。
Push Notification Service 在推送通知发送后向你的 web service 发送一个回复码.然而, Push Notification Service 不能为你的推送提醒是否成功传递到应用程序提供端到端的确认.了解更多信息,请参考 Push Notification Service Response Codes for Windows Phone推送消息过程:
图 2
- WP设备到MSNS注册PN服务,并得到唯一的服务URI
- WP设备把服务URI传递给Cloud服务,并注册
- 当有更新消息发送时,Cloud服务往MSNS服务发送更新消息
- MSNS把更新消息发送到WP设备
- 需要时WP设备往Cloud服务读取更多的数据
推送通知服务类型:
- Raw Notification
- 可以发送任何格式的数据
- 应用程序可以根据需要加工数据
- 应用程序相关(Application-specific)的通知消息
- 只有在应用程序运行时,才发送
- Toast Notification
- 发送的数据为指定的XML格式
- 如果应用程序正在运行,内容发送到应用程序中
-
如果应用程序不在运行,弹出Toast消息框显示消息
- App图标加上2个文本描述
- 打断用户当前操作,但这是临时的
- 用户可以点击进行跟踪
- Title Notification
- 发送的数据为指定的XML格式
- 不会往应用程序进行发送
- 如果用户把应用程序pin to start,那么更新数据发送到start screen的titile里
- 包含三个属性,背景,标题和计算器
- 每个属性都有固定的格式与位置
- 可以使用其中的属性,不一定三个属性一起使用
示例1:(摘自Webcast)
首先建立服务端Windows窗体程序,相当于图2中的Your Cloud Application,界面如下
加入“发送”按钮事件:
{
string msg = String.Format( " {0}{1}, {2}度 " , LocationComboBox.Text,
WeatherComboBox.Text, TemperatureTextBox.Text);
string type = NotificationTypeComboBox.Text as string ;
if (type == " Raw " )
{
byte [] strBytes = new UTF8Encoding().GetBytes(msg);
SendRawNotification(strBytes);
}
else 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);
SendToastNotification(strBytes);
}
else if (type == " Tile " )
{
string tileMessage = " <?xml version=\"1.0\" encoding=\"utf-8\"?> " +
" <wp:Notification xmlns:wp=\"WPNotification\"> " +
" <wp:Tile> " +
" <wp:BackgroundImage>/Images/ " + WeatherComboBox.Text + " .png</wp:BackgroundImage> " +
" <wp:Count> " + TemperatureTextBox.Text + " </wp:Count> " +
" <wp:Title> " + LocationComboBox.Text + " </wp:Title> " +
" </wp:Tile> " +
" </wp:Notification> " ;
byte [] strBytes = new UTF8Encoding().GetBytes(tileMessage);
SendTileNotification(strBytes);
}
}
加入Raw Notification的方法:
{
// The URI that the Push Notification Service returns to the Push Client when creating a notification channel.
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(NotificationUriTextBox.Text);
// HTTP POST is the only allowed method to send the notification.
sendNotificationRequest.Method = WebRequestMethods.Http.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();
// Sets raw notification
sendNotificationRequest.ContentType = " text/xml; charset=utf-8 " ;
sendNotificationRequest.Headers.Add( " X-NotificationClass " , " 3 " );
// Possible batching interval values:
// 3: The message is delivered by the Push Notification Service immediately.
// 13: The message is delivered by the Push Notification Service within 450 seconds.
// 23: The message is delivered by the Push Notification Service within 900 seconds.
// Sets the web request content length.
sendNotificationRequest.ContentLength = Payload.Length;
// Sets the notification payload to send.
byte [] notificationMessage = Payload;
// Sends the notification.
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0 , notificationMessage.Length);
}
// Gets 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 " ];
MsgLabel.Text = String.Format( " 通知状态:{0},管道状态:{1},设备状态:{2} " ,
notificationStatus, notificationChannelStatus, deviceConnectionStatus);
}
加入Toast Notification类型的方法:
{
// The URI that the Push Notification Service returns to the Push Client when creating a notification channel.
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(NotificationUriTextBox.Text);
// HTTP POST is the only allowed method to send the notification.
sendNotificationRequest.Method = WebRequestMethods.Http.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();
// Sets toast notification
sendNotificationRequest.ContentType = " text/xml; charset=utf-8 " ;
sendNotificationRequest.Headers.Add( " X-WindowsPhone-Target " , " toast " );
sendNotificationRequest.Headers.Add( " X-NotificationClass " , " 2 " );
// Possible batching interval values:
// 2: The message is delivered by the Push Notification Service immediately.
// 12: The message is delivered by the Push Notification Service within 450 seconds.
// 22: The message is delivered by the Push Notification Service within 900 seconds.
// Sets the web request content length.
sendNotificationRequest.ContentLength = Payload.Length;
// Sets the notification payload to send.
byte [] notificationMessage = Payload;
// Sends the notification.
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0 , notificationMessage.Length);
}
// Gets 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 " ];
MsgLabel.Text = String.Format( " 通知状态:{0},管道状态:{1},设备状态:{2} " ,
notificationStatus, notificationChannelStatus, deviceConnectionStatus);
}
加入Tile Notification类型的方法:
{
// The URI that the Push Notification Service returns to the Push Client when creating a notification channel.
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(NotificationUriTextBox.Text);
// HTTP POST is the only allowed method to send the notification.
sendNotificationRequest.Method = WebRequestMethods.Http.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();
// Sets toast notification
sendNotificationRequest.ContentType = " text/xml; charset=utf-8 " ;
sendNotificationRequest.Headers.Add( " X-WindowsPhone-Target " , " token " );
sendNotificationRequest.Headers.Add( " X-NotificationClass " , " 1 " );
// Possible batching interval values:
// 1: The message is delivered by the Push Notification Service immediately.
// 11: The message is delivered by the Push Notification Service within 450 seconds.
// 21: The message is delivered by the Push Notification Service within 900 seconds.
// Sets the web request content length.
sendNotificationRequest.ContentLength = Payload.Length;
// Sets the notification payload to send.
byte [] notificationMessage = Payload;
// Sends the notification.
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0 , notificationMessage.Length);
}
// Gets 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 " ];
MsgLabel.Text = String.Format( " 通知状态:{0},管道状态:{1},设备状态:{2} " ,
notificationStatus, notificationChannelStatus, deviceConnectionStatus);
}
新建Windows Phone Application,界面如下图所示:
在MainPage.xaml.cs类中添加字段代码
private const string channelName = " Channel1 " ;
添加 “连接”按钮事件代码
{
httpChannel = HttpNotificationChannel.Find(channelName);
// Delete the Channel if exists
if (httpChannel != null )
{
httpChannel.Close();
httpChannel.Dispose();
}
// Create the channel
httpChannel = new HttpNotificationChannel(channelName, " NotificationService " );
// Register to UriUpdated event - occurs when channel successfully opens
// WP设备到MSNS注册PN服务,并得到唯一的服务URI
httpChannel.ChannelUriUpdated += new EventHandler < NotificationChannelUriEventArgs > (httpChannel_ChannelUriUpdated);
// general error handling for push channel
httpChannel.ErrorOccurred += new EventHandler < NotificationChannelErrorEventArgs > (httpChannel_ErrorOccurred);
// Subscribed to Raw Notification
httpChannel.HttpNotificationReceived += new EventHandler < HttpNotificationEventArgs > (httpChannel_HttpNotificationReceived);
// subscrive to toast notification when running app
// 程序运行时收到消息
httpChannel.ShellToastNotificationReceived += new EventHandler < NotificationEventArgs > (httpChannel_ShellToastNotificationReceived);
// Open the channel
httpChannel.Open();
// subscrive to toast notification
// 程序没有运行时,消息会在WP设备顶部弹出显示
httpChannel.BindToShellToast();
// subscrive to tile notification
httpChannel.BindToShellTile();
}
添加事件代码
{
string msg = "" ;
foreach (var key in e.Collection.Keys)
{
msg += key + " : " + e.Collection[key] + Environment.NewLine;
}
Dispatcher.BeginInvoke(() =>
{
MsgTextBlock.Text = msg;
});
}
void httpChannel_HttpNotificationReceived( object sender, HttpNotificationEventArgs e)
{
using (var reader = new StreamReader(e.Notification.Body))
{
string msg = reader.ReadToEnd();
Dispatcher.BeginInvoke(() =>
{
MsgTextBlock.Text = msg;
});
}
}
void httpChannel_ErrorOccurred( object sender, NotificationChannelErrorEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
MsgTextBlock.Text = e.Message;
});
}
void httpChannel_ChannelUriUpdated( object sender, NotificationChannelUriEventArgs e)
{
Debug.WriteLine( " ChannelUri: {0} " , e.ChannelUri);
}
示例2:
本示例演示了Toast Notification的使用。示例包含3个项目:
1.WCF服务应用程序,模拟Cloud Application
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfService1
{
[ServiceContract]
public interface IService1
{
[OperationContract]
void Subscribe( string uri);
[OperationContract]
void SendToast( string title, string message);
}
}
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Net;
namespace WcfService1
{
public class Service1 : IService1
{
private static Uri uri;
public void Subscribe( string subscriberUri)
{
uri = new Uri(subscriberUri);
}
public void SendToast( string title, string message)
{
string toastMessage = " <?xml version=\"1.0\" encoding=\"utf-8\"?> " +
" <wp:Notification xmlns:wp=\"WPNotification\"> " +
" <wp:Toast> " +
" <wp:Text1> " + title + " </wp:Text1> " +
" <wp:Text2> " + message + " </wp:Text2> " +
" </wp:Toast> " +
" </wp:Notification> " ;
byte [] messageBytes = System.Text.Encoding.UTF8.GetBytes(toastMessage);
SendMessage(uri, messageBytes);
}
private static void SendMessage(Uri uri, byte [] message)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = WebRequestMethods.Http.Post;
request.ContentType = " text/xml " ;
request.ContentLength = message.Length;
request.Headers.Add( " X-MessageID " , Guid.NewGuid().ToString());
request.Headers[ " X-WindowsPhone-Target " ] = " toast " ;
request.Headers.Add( " X-NotificationClass " , " 2 " );
var requestStream = request.GetRequestStream();
requestStream.Write(message, 0 , message.Length);
}
}
}
2.WPF应用程序,消息推送触发程序
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
Title = " MainWindow " Height = " 350 " Width = " 525 " >
< Grid >
< Button Content = " Button " Height = " 23 " HorizontalAlignment = " Left " Margin = " 218,101,0,0 " Name = " button1 " VerticalAlignment = " Top " Width = " 75 " Click = " button1_Click " />
</ Grid >
</ Window >
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click( object sender, RoutedEventArgs e)
{
ServiceReference1.Service1Client svc = new ServiceReference1.Service1Client();
svc.SendToast( " Check app " , " new data has arrived " );
}
}
}
< configuration >
< system.serviceModel >
< bindings >
< basicHttpBinding >
< binding name = " BasicHttpBinding_IService1 " closeTimeout = " 00:01:00 "
openTimeout = " 00:01:00 " receiveTimeout = " 00:10:00 " sendTimeout = " 00:01:00 "
allowCookies = " false " bypassProxyOnLocal = " false " hostNameComparisonMode = " StrongWildcard "
maxBufferSize = " 65536 " maxBufferPoolSize = " 524288 " maxReceivedMessageSize = " 65536 "
messageEncoding = " Text " textEncoding = " utf-8 " transferMode = " Buffered "
useDefaultWebProxy = " true " >
< readerQuotas maxDepth = " 32 " maxStringContentLength = " 8192 " maxArrayLength = " 16384 "
maxBytesPerRead = " 4096 " maxNameTableCharCount = " 16384 " />
< security mode = " None " >
< transport clientCredentialType = " None " proxyCredentialType = " None "
realm = "" />
< message clientCredentialType = " UserName " algorithmSuite = " Default " />
</ security >
</ binding >
</ basicHttpBinding >
</ bindings >
< client >
< endpoint address = " http://localhost:10317/Service1.svc " binding = " basicHttpBinding "
bindingConfiguration = " BasicHttpBinding_IService1 " contract = " ServiceReference1.IService1 "
name = " BasicHttpBinding_IService1 " />
</ client >
</ system.serviceModel >
</ configuration >
3.Windows Phone Application,手机接收程序
x:Class = " WindowsPhoneApplication1.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 "
FontFamily = " {StaticResource PhoneFontFamilyNormal} "
FontSize = " {StaticResource PhoneFontSizeNormal} "
Foreground = " {StaticResource PhoneForegroundBrush} "
SupportedOrientations = " Portrait " Orientation = " Portrait "
mc:Ignorable = " d " d:DesignWidth = " 480 " d:DesignHeight = " 768 "
shell:SystemTray.IsVisible = " True " >
<!-- LayoutRoot contains the root grid where all other 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 = " 24,24,0,12 " >
< TextBlock x:Name = " ApplicationTitle " Text = " MY APPLICATION " Style = " {StaticResource PhoneTextNormalStyle} " />
< TextBlock x:Name = " PageTitle " Text = " page name " Margin = " -3,-8,0,0 " Style = " {StaticResource PhoneTextTitle1Style} " />
</ StackPanel >
<!-- ContentPanel - place additional content here -->
< Grid x:Name = " ContentGrid " Grid.Row = " 1 " >
< TextBlock Height = " 276 " HorizontalAlignment = " Left " Margin = " 48,108,0,0 " Name = " textBlock1 " Text = " TextBlock " VerticalAlignment = " Top " Width = " 350 " />
</ Grid >
</ Grid >
</ phone:PhoneApplicationPage >
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Notification;
namespace WindowsPhoneApplication1
{
public partial class MainPage : PhoneApplicationPage
{
private HttpNotificationChannel channel;
private const string ChannelName = " MyChannel " ;
public MainPage()
{
InitializeComponent();
SetupNotificationChannel();
}
private void SetupNotificationChannel()
{
channel = HttpNotificationChannel.Find(ChannelName);
if (channel == null )
{
channel = new HttpNotificationChannel(ChannelName);
channel.ChannelUriUpdated += new EventHandler < NotificationChannelUriEventArgs > (channel_ChannelUriUpdated);
channel.Open();
}
else
{
RegisterForNotifications();
}
}
void channel_ChannelUriUpdated( object sender, NotificationChannelUriEventArgs e)
{
channel = HttpNotificationChannel.Find(ChannelName);
channel.BindToShellToast();
RegisterForNotifications();
}
private void RegisterForNotifications()
{
ServiceReference1.Service1Client svc = new ServiceReference1.Service1Client();
svc.SubscribeAsync(channel.ChannelUri.ToString());
channel.ShellToastNotificationReceived += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => ToastReceived(e));
}
private void ToastReceived(NotificationEventArgs e)
{
textBlock1.Text = string .Format( " Title: " + e.Collection[ " wp:Text1 " ] + " \nMessage: " + e.Collection[ " wp:Text2 " ]);
}
}
}
示例3:Windows Phone 7 SDK
微软提供的SDK示例,可以访问参考链接4 ,查看完整的示例代码。
参考链接:
2.Code Download