Windows Phone 推送通知的第四类推送

 

    在 MSDN 文档有关 Windows Phone 推送通知 有关推送的内容包含 Tile、Toast、Raw 这三种通知。这三种通知

的方式类似,运用的场合不同,这里不再赘述,它们的运行原理类似:

Windows Phone 推送通知的第四类推送

 

    其实 有关 VoIP 的文档 中,有一个叫 VoipHttpIncomingCallTask 的后台代理:在推送通知通道收到新的传入呼叫时启动,

它使 Windows Phone 运行时 程序集了解到它应该创建新的呼叫。

    据说 VoIP 的推送通知是属于强推送的,可靠性比常用的三种要强一些,有关这几种推送的可靠性我没有测试过,不知道是不是

因为微软给 VoIP 的推送通知所使用服务器更可靠一些,纯属猜测。不过像 Skype 这种使用 VoIP 的网络电话,对于呼叫的响应

确实需要比较可靠的消息通知。

 

在这个代理运行的时候,可以在代理的 OnInvoke(ScheduledTask task) 方法中通过:

VoipHttpIncomingCallTask incomingCallTask = task as VoipHttpIncomingCallTask;

if (incomingCallTask != null)

 {

         // 把推送通知中自定义的 xml 文本转换成该对象

         Notification pushNotification;

         using (MemoryStream ms = new MemoryStream(incomingCallTask.MessageBody))

         {

            XmlSerializer xs = new XmlSerializer(typeof(Notification));

            pushNotification = (Notification)xs.Deserialize(ms);//反序列化消息的 xml

          }

}

中的 incomingCallTask.MessageBody 获取推送的消息内容,并且这个消息体是可以自定义的,然后可以作为 Toast 或者 Tile 通知显示给用户。作为消息通知,有关 VoIP 的其它的步骤就不再继续了。

  

 MSDN 上介绍的 Toast 和 Tile 通知的步骤:

1)  首先,客户端 app 需要向微软的推送服务器申请 push channel,微软服务器返回 channel 后,客户端把这个 channel 发送到第三方服务器

2)  当第三方服务器需要向用户推送消息时,第三方服务器只需向微软的推送服务器发送 http 数据请求,然后微软的推送服务器再把消息推送到客户端 

VoIP 的呼入推送和上面的步骤类似,不同的是客户端代码工程需要创建并添加一个 VoipHttpIncomingCallTask 代理: 

// 创建一个新的呼叫代理

 VoipHttpIncomingCallTask incomingCallTask = new VoipHttpIncomingCallTask(代理名称, 推送通道);

 incomingCallTask.Description = "Incoming call task";

 ScheduledActionService.Add(incomingCallTask);

 

当客户端收到 VoIP 的推送通知时,会调用代理的  OnInvoke(ScheduledTask task) 方法,就可以在这个方法里面获取上面所说的 incomingCallTask.MessageBody 属性中获取消息通知了。

  

第三方服务器端在发送消息的时候,HttpWebRequest 对象发送 http 请求时,请求报文头:

1) tile 通知:Request.Headers.Add("X-NotificationClass", "1");

2) toast 通知:Request.Headers.Add("X-NotificationClass", "2");

3) raw 通知:Request.Headers.Add("X-NotificationClass", "3");

4) voip 通知:Request.Headers.Add("X-NotificationClass", "4")

并且 VoIP 的消息的报文体可以是自定义的,例如:

Text = @"<?xml version=""1.0"" encoding=""utf-8""?>

        <wp:Notification xmlns:wp=""WPNotification"">

            <TitleField>北京天安门</TitleField>

            <BodyField>叙利亚可能遭受美国武力打击</BodyField>

        </wp:Notification>";

 客户端定义相同的字段,用于反序列化:

    public partial class Notification

    {

        public string TitleField

        { get; set; }

       public string BodyField

        { get; set; }

   }

  

客户端和服务器端制定相同的可序列化数据类型进行通信。

    这篇文章介绍的工程运行交互大概是:

Windows Phone 推送通知的第四类推送

Windows Phone 推送通知的第四类推送

Windows Phone 推送通知的第四类推送

 

     使用 VoIP 中的 VoipHttpIncomingCallTask 代理进行消息推送时,可以同时显示 Toast 或者 Tile 通知,另一个

好处是,在之前的 Tile通知中,只能更新app的主瓷贴,而次级瓷贴没办法显示通知,使用这个代理时,可以自定义

更新次级瓷贴的消息。

 

第一步,新建一个wpf 的 pc 端程序,作为推送通知的服务器,运行截图如上面3、中显示的。

      页面中的 xaml:

        <TextBox x:Name="txtPushChannel" HorizontalAlignment="Left" Height="80" Margin="109,22,0,0" 
TextWrapping
="Wrap" Text="TextBox" VerticalAlignment="Top" Width="334"/> <TextBlock HorizontalAlignment="Left" Margin="42,22,0,0" TextWrapping="Wrap" Text="Channel:"
VerticalAlignment
="Top"/> <TextBox x:Name="txtXml" HorizontalAlignment="Left" Height="107" Margin="109,149,0,0" TextWrapping="Wrap"
Text
="TextBox" VerticalAlignment="Top" Width="334"/> <TextBlock HorizontalAlignment="Left" Margin="58,149,0,0" TextWrapping="Wrap" Text="xml:"
VerticalAlignment
="Top" RenderTransformOrigin="0.283,0.387"/> <Button Content="Push" HorizontalAlignment="Left" Margin="205,272,0,0" VerticalAlignment="Top"
Width="75" Click="Button_Click"/>

 

  相应的 C#,作用是向微软的推送服务器发送推送通知,微软的服务器就会把消息推送到注册了该 channel 的手机上面:

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

            txtXml.Text = @"<?xml version=""1.0"" encoding=""utf-8""?>

                           <wp:Notification xmlns:wp=""WPNotification"">

                               <TitleField>北京天安门</TitleField>

                               <BodyField>叙利亚可能遭受美国武力打击</BodyField>

                           </wp:Notification>";



            txtPushChannel.Text = "http://db3.notify.live.net/throttledthirdparty/01.00/AQEcdarEW-cRQIsjVBGEMoHVAgAAAAAD
tA8DAAQUZm52OjI0MjhFRkVEMUVERTE0MzAFBkxFR0FDWQ
";//推送通道 } private void Button_Click(object sender, RoutedEventArgs e) { // 向这个 channel URI 发送推送通知,模拟 VoIP 呼叫 try { // 只能使用 HTTP POST 方式向微软的推送服务器发送通知 HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(txtPushChannel.Text); sendNotificationRequest.Method = "POST"; // 自定义消息 byte[] notificationMessage = Encoding.UTF8.GetBytes(txtXml.Text); // 设置请求报文头 sendNotificationRequest.ContentLength = notificationMessage.Length; sendNotificationRequest.ContentType = "text/xml"; sendNotificationRequest.Headers["X-NotificationClass"] = "4"; // Class 4 indicates an incoming VoIP call // 发送请求报文体 sendNotificationRequest.BeginGetRequestStream((IAsyncResult arRequest) => { try { using (Stream requestStream = sendNotificationRequest.EndGetRequestStream(arRequest)) { requestStream.Write(notificationMessage, 0, notificationMessage.Length); } // 获取微软服务器的响应 sendNotificationRequest.BeginGetResponse((IAsyncResult arResponse) => { try { HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.EndGetResponse(arResponse); string notificationStatus = response.Headers["X-NotificationStatus"]; string subscriptionStatus = response.Headers["X-SubscriptionStatus"]; string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"]; // 显示微软服务器返回的发送状态 this.ShowResult(string.Format("Notification: {0}\r\nSubscription: {1}\r\nDevice: {2}",
notificationStatus, subscriptionStatus, deviceConnectionStatus)); }
catch (Exception ex) { this.ShowResult(ex); } }, null); } catch (Exception ex) { this.ShowResult(ex); } }, null); } catch (Exception ex) { this.ShowResult(ex); } } // 显示弹出框消息 private void ShowResult(object result) { this.Dispatcher.BeginInvoke((Action)(() => { Exception ex = result as Exception; if (ex == null) { MessageBox.Show(string.Format("{0}\r\n{1}", result, DateTime.Now)); } else { MessageBox.Show(string.Format("An error has occurred\r\n{0}", DateTime.Now)); } }), null); } }

 

第二步,新建一个 WP 客户端程序,默认名称 PhoneApp1,运行截图如上面 1、中所示。

 

    新建一个 VoipHttpIncomingCallTask 后台代理工程,作用是 接收传入呼叫通知时启动该后台代理,

然后显示 Toast 通知和 Tile 通知:

Windows Phone 推送通知的第四类推送

    该代理的全部代码:

   public class ScheduledAgent : ScheduledTaskAgent

    {

        /// <remarks>

        /// ScheduledAgent 构造函数,初始化 UnhandledException 处理程序

        /// </remarks>

        static ScheduledAgent()

        {

            // 订阅托管的异常处理程序

            Deployment.Current.Dispatcher.BeginInvoke(delegate

            {

                Application.Current.UnhandledException += UnhandledException;

            });

        }



        /// 出现未处理的异常时执行的代码

        private static void UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)

        {

            if (Debugger.IsAttached)

            {

                // 出现未处理的异常;强行进入调试器

                Debugger.Break();

            }

        }



        /// <summary>

        /// 运行计划任务的代理

        /// </summary>

        /// <param name="task">

        /// 调用的任务

        /// </param>

        /// <remarks>

        /// 调用定期或资源密集型任务时调用此方法

        /// </remarks>

        protected override void OnInvoke(ScheduledTask task)

        {

            //TODO: 添加用于在后台执行任务的代码

            VoipHttpIncomingCallTask incomingCallTask = task as VoipHttpIncomingCallTask;

            if (incomingCallTask != null)

            {

               

                // 把推送通知中自定义的 xml 文本转换成该对象

                Notification pushNotification;

                using (MemoryStream ms = new MemoryStream(incomingCallTask.MessageBody))

                {

                    XmlSerializer xs = new XmlSerializer(typeof(Notification));

                    pushNotification = (Notification)xs.Deserialize(ms);//反序列化消息的 xml

                }



                Debug.WriteLine(" Incoming call from caller {0}, number {1}", pushNotification.TitleField, pushNotification.BodyField);



                // 显示通知

                ChangeMainTile(pushNotification.TitleField, pushNotification.BodyField);

            }

            NotifyComplete();//通知操作系统,代理已针对代理的当前调用完成其目标任务。

        }





        // 同时显示 Tile 通知和 Toast 通知(app的主瓷贴需要订到开始菜单中)

        void ChangeMainTile(string strTitle, string strBody)

        {

            // Toast 通知

            ShellToast to = new ShellToast();

            to.Title = strTitle;

            to.Content = strBody;



            to.Show();



            // 瓷贴通知,第一个瓷贴为 app 的主瓷贴

            ShellTile defaultTile = ShellTile.ActiveTiles.First();

            StandardTileData tileData = new StandardTileData()

            {

                Title = strTitle,

                Count = 0,

                BackTitle = strTitle,

                BackContent = strBody

            };



            defaultTile.Update(tileData);

        }

    }
View Code
    //自定义消息的字段

    [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.17613")]

    [System.Diagnostics.DebuggerStepThroughAttribute()]

    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "WPNotification")]

    [System.Xml.Serialization.XmlRootAttribute(Namespace = "WPNotification", IsNullable = false)]

    public partial class Notification

    {



        private string titleField;



        private string bodyField;



        //消息标题

        [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]

        public string TitleField

        {

            get

            {

                return this.titleField;

            }

            set

            {

                this.titleField = value;

            }

        }



        //消息体

        [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]

        public string BodyField

        {

            get

            {

                return this.bodyField;

            }

            set

            {

                this.bodyField = value;

            }

        }

    }
View Code

 

第三步,把第二步中的代理添加引用到 PhoneApp1 工程中:

Windows Phone 推送通知的第四类推送

 

   在 MainPage 中添加一个文本框和一个按钮:

  <TextBox x:Name="txtRegist" HorizontalAlignment="Left" Height="318" Margin="23,125,-23,0" 
TextWrapping
="Wrap" Text="channel" VerticalAlignment="Top" Width="456"/> <Button Content="Regist" HorizontalAlignment="Left" Margin="113,495,0,0"
VerticalAlignment
="Top" Width="265" Click="Button_Click"/>


  在 MainPage 的 codebehind 页面中,添加注册 push channel 逻辑:

 public partial class MainPage : PhoneApplicationPage

    {

        // 构造函数

        public MainPage()

        {

            InitializeComponent();

        }



        // VoIP 的呼入任务的名称

         static string incomingCallTaskName = "PhoneVoIPApp.IncomingCallTask";

        // 推送通知的通道名称

         static string pushChannelName = "VoIPChannel";

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            InitPushChannel();

        }



        private void InitPushChannel()

        {

            // 查找以前注册的推送通知通道

            HttpNotificationChannel httpChannel = HttpNotificationChannel.Find(pushChannelName);



            // 如果以前没有注册通道,则创建一个新通道

            if (httpChannel == null || httpChannel.ChannelUri == null)

            {

                httpChannel = new HttpNotificationChannel(pushChannelName);

                httpChannel.Open();

            }

            else

            {

                // 已经存在的推送通道

                pushChannelName = httpChannel.ChannelUri.ToString();

                Debug.WriteLine("[App] Existing Push channel URI is {0}", pushChannelName);

            }



            // 注册相应的事件

            httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);

            httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);

            httpChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);

        }



        private void PushChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)

        {

            Debug.WriteLine("[App] New Push channel URI is {0}", e.ChannelUri);



            InitHttpNotificationTask(); // 初始化 VoIP 呼入通知的代理



            this.Dispatcher.BeginInvoke(delegate { txtRegist.Text = e.ChannelUri + ""; });

          

            // TODO: Let your cloud server know that the push channel to this device is e.ChannelUri.

        }



        private void PushChannel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)

        {

            // TODO: Let your cloud server know that the push channel to this device is no longer valid.

        }



        private void PushChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)

        {

            // TODO: Process raw push notifications here, if required.

        }





        public void InitHttpNotificationTask()

        {

            // 获取已经存在的 VoIP 呼入代理

            VoipHttpIncomingCallTask incomingCallTask = ScheduledActionService.Find(incomingCallTaskName) as VoipHttpIncomingCallTask;

            if (incomingCallTask != null)

            {

                if (incomingCallTask.IsScheduled == false)

                {

                    // 从计划操作服务中删除具有指定名称的 ScheduledAction

                    ScheduledActionService.Remove(incomingCallTaskName);

                }

                else

                {

                    // 计划任务已经添加,并且计划状态为 true,则直接返回

                    return;

                }

            }



            // 创建一个新的呼叫代理

            incomingCallTask = new VoipHttpIncomingCallTask(incomingCallTaskName, pushChannelName);

            incomingCallTask.Description = "Incoming call task";

            ScheduledActionService.Add(incomingCallTask);

        }

    }


   代码工程完成,源码下载

 

   这个工程是参考微软的 VoIP 代码完成的,具体关于 VoIP 的代码: ChatterBox VoIP sample app

你可能感兴趣的:(windows phone)