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

 

    在 MSDN 文檔有關 Windows Phone 推送通知 有關推送的內容包含 Tile、Toast、Raw 這三種通知。這三種通知windows

的方式相似,運用的場合不一樣,這裏再也不贅述,它們的運行原理相似:服務器

 

    其實 有關 VoIP 的文檔 中,有一個叫 VoipHttpIncomingCallTask 的後臺代理:在推送通知通道收到新的傳入呼叫時啓動,網絡

它使 Windows Phone 運行時 程序集了解到它應該建立新的呼叫。app

    聽說 VoIP 的推送通知是屬於強推送的,可靠性比經常使用的三種要強一些,有關這幾種推送的可靠性我沒有測試過,不知道是否是ide

由於微軟給 VoIP 的推送通知所使用服務器更可靠一些,純屬猜想。不過像 Skype 這種使用 VoIP 的網絡電話,對於呼叫的響應函數

確實須要比較可靠的消息通知。測試

 

在這個代理運行的時候,能夠在代理的 OnInvoke(ScheduledTask task) 方法中經過:ui

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 的其它的步驟就再也不繼續了。this

  

 MSDN 上介紹的 Toast 和 Tile 通知的步驟:spa

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; }
   }

  

客戶端和服務器端制定相同的可序列化數據類型進行通訊。

    這篇文章介紹的工程運行交互大概是:

 

     使用 VoIP 中的 VoipHttpIncomingCallTask 代理進行消息推送時,能夠同時顯示 Toast 或者 Tile 通知,另外一個

好處是,在以前的 Tile通知中,只能更新app的主瓷貼,而次級瓷貼沒辦法顯示通知,使用這個代理時,能夠自定義

更新次級瓷貼的消息。

 

第一步,新建一個wpf 的 pc 端程序,做爲推送通知的服務器,運行截圖如上面三、中顯示的。

      頁面中的 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,運行截圖如上面 一、中所示。

 

    新建一個 VoipHttpIncomingCallTask 後臺代理工程,做用是 接收傳入呼叫通知時啓動該後臺代理,

而後顯示 Toast 通知和 Tile 通知:

    該代理的所有代碼:

   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

相關文章
相關標籤/搜索