Windows Phone 推送通知的第四类推送_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > Windows Phone 推送通知的第四类推送

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

 2013/9/9 16:07:07  段博琼  博客园  我要评论(0)
  • 摘要:在MSDN文档有关WindowsPhone推送通知有关推送的内容包含Tile、Toast、Raw这三种通知。这三种通知的方式类似,运用的场合不同,这里不再赘述,它们的运行原理类似:其实有关VoIP的文档中,有一个叫VoipHttpIncomingCallTask的后台代理:在推送通知通道收到新的传入呼叫时启动,它使WindowsPhone运行时程序集了解到它应该创建新的呼叫。据说VoIP的推送通知是属于强推送的,可靠性比常用的三种要强一些,有关这几种推送的可靠性我没有测试过
  • 标签:Windows

 

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

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

 

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

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

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

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

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

 

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

 

     使用 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 通知:

    该代理的全部代码:

logs_code_hide('8b15b49b-48cf-422a-8084-7b108bce70dd',event)" src="/Upload/Images/2013090916/2B1B950FA3DF188F.gif" alt="" />
   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 工程中:

 

   在 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

发表评论
用户名: 匿名