推送通知服务
概述
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