Windows Phone開(kāi)發(fā)(43):推送通知第一集之Toast推送
好像有好幾天沒(méi)更新了,抱歉抱歉,最近“光榮”地失業(yè),先是忙于尋找新去處,唉,暫時(shí)沒(méi)有下文。而后又有一些瑣事要辦,不過(guò)不要緊,今天咱們繼續(xù)。
動(dòng)畫的內(nèi)容就告一段落,本系列文章只作簡(jiǎn)單引導(dǎo),不會(huì)覆蓋每一個(gè)細(xì)節(jié),最終能不能學(xué)好,就完全看各位自己了。
從本節(jié)開(kāi)始,我們將討論推送通知,這個(gè)東西不太好理解,而推送通知的原理和過(guò)程,如果你看MSDN的示意圖,相信你會(huì)有點(diǎn)暈,若不,我帖出給你看看。
算了,不帖,不知怎么回事,上傳不了圖片。
現(xiàn)補(bǔ)上圖片。
那么,我就說(shuō)一個(gè)故事吧,希望能幫助你理解何為推送通知。
上大學(xué)的時(shí)候,我很喜歡到圖書館借書,然后,晚上在宿舍里看,一直看到累了就睡覺(jué)。有一回,我發(fā)現(xiàn)一本好書叫《中國(guó)式商道》,結(jié)果呢,去圖書館沒(méi)找著,但是查一下是有的,我很看這本書,就去問(wèn)管理員,管理員說(shuō)可能被別人借了。
這時(shí)候我心里想:那就每天來(lái)看一下有沒(méi)有在書架上,有再借。
管理員似乎猜到了我的心思,他說(shuō):“這位同學(xué),你可以留下借書證號(hào)和聯(lián)系方式,如果你真想看那本書,一旦有人來(lái)還書了,我馬上通知你,你不必天天來(lái)找。”
我連忙說(shuō)謝謝。
比如,我開(kāi)好了應(yīng)用程序A,用戶B的手機(jī)正在使用我的應(yīng)用程序,但有時(shí)候我會(huì)發(fā)一些通知給用戶B手機(jī),例如,增加新功能或修復(fù)某些Bug,或者有公益活邀請(qǐng)用戶參加等。但是,用戶B上的應(yīng)用程序如何才知道有新消息呢?
按照傳統(tǒng)的做法,在應(yīng)用程序中做一個(gè)定時(shí)“炸彈”,每隔一段時(shí)間通過(guò)網(wǎng)絡(luò)訪問(wèn)一下我的服務(wù)器,檢索一下有沒(méi)有新消息,然后把結(jié)果返回給客戶端應(yīng)用程序。你想想,這樣做的缺點(diǎn)是什么?
經(jīng)常訪問(wèn)網(wǎng)絡(luò),增加網(wǎng)絡(luò)流量,也會(huì)消耗一定的電量和資源,如果我用GPRS上網(wǎng),那就倒霉了。
但是,如果我的客戶端從來(lái)不需要主動(dòng)訪問(wèn)網(wǎng)絡(luò)呢,我也不必在應(yīng)用程序中放置計(jì)時(shí)器,程序無(wú)須訪問(wèn)網(wǎng)絡(luò),我的新消息不是發(fā)送到用戶手機(jī),而是發(fā)送到微 軟的云服務(wù)器,然后由云服務(wù)器把消息推送到用戶手機(jī)。這樣就好比前面的例子,我不用天天跑去圖書館找書,只要有那本書,圖書館管理員就把電話找我。你說(shuō), 這樣是不是既省心也省力了?
推送通知有三種:Toast通知,磁帖通知和自定義通知。前面兩種都是死的,都是被硬性規(guī)定的,你不要問(wèn)為什么,記住就行了。而第三種即Raw通知,這種通知方式比較靈活,你可以自定義其格式和內(nèi)容。
今天,我們來(lái)了解第一種通知——Toast。
這是什么呢?
本想截個(gè)圖的,但不知道啥事,就是上傳不了,沒(méi)反應(yīng),CSDN的博客經(jīng)常出問(wèn)題。那沒(méi)辦法了,我用文字描述一下吧,Toast通知就是在應(yīng)用程序沒(méi)有在前臺(tái)運(yùn)行時(shí),如果收到Toast通知,會(huì)在屏幕最上方顯示一條提示信息,就和我們收到短信時(shí)一樣。
微軟的云服務(wù)器會(huì)為我們的手機(jī)分配一個(gè)URL,就在侈的應(yīng)用程序注冊(cè)推送通道后更新的,云服務(wù)器就是利用這個(gè)URL來(lái)找到你的手機(jī)并把通知發(fā)到手機(jī) 上,就像前面例子中,我 留下借書證編號(hào)和電話號(hào)碼,到時(shí)候,管理員可以通過(guò)手機(jī)號(hào)碼來(lái)聯(lián)系我。實(shí)際開(kāi)發(fā)在,你可以通過(guò)各種方式把這個(gè)URL傳到你的服務(wù)器上保存,因?yàn)榘l(fā)送推送通 知是需要這個(gè)URL的。
一般來(lái)說(shuō),如果你建有自己的服務(wù)器,就應(yīng)該會(huì)有一個(gè)固定的IP地址或域名,你不妨通過(guò)HTTP方式把用戶手機(jī)的URL發(fā)送到你的服務(wù)器保存。
那么,如何發(fā)送推送通知呢?不要被嚇倒,其實(shí)很簡(jiǎn)單,就是平常我們熟悉的POST方式提交一個(gè)HTTP請(qǐng)求罷了,而提交的URL就是從云服務(wù)器中得到的URL。而POST的內(nèi)容就是一個(gè)XML文檔。Toast推送通知的格式如下:
- <?xml version="1.0" encoding="utf-8" ?>
- <wp:Notification xmlns:wp="WPNotification">
- <wp:Toast>
- <wp:Text1>文本一</wp:Text1>
- <wp:Text2>文本二</wp:Text2>
- <wp:Param>參數(shù)</wp:Param>
- </wp:Toast>
- </wp:Notification>
這是固定的格式,不要問(wèn)我為什么,它就是死的。“文本一”指的是顯示Toast提示的標(biāo)題,“本文二”自然就是正文了,文字盡量簡(jiǎn)單,最好幾個(gè)字搞定。
而“參數(shù)”呢?它其它是一個(gè)URI,這個(gè)URI就是當(dāng)用戶點(diǎn)擊了Toast消息后啟動(dòng)應(yīng)用程序時(shí)導(dǎo)航到的頁(yè)面,這個(gè)與前面我們說(shuō)到的“次要磁帖”是一樣的。舉幾個(gè)例子吧。
/MainPage.xaml
/MainPage.xa/Mml?v=12345
/MainPage.xaml?value1=123&value2=abcd
最后一條其實(shí)就是value1=123&value2=abc,別忘了是XML文檔,字符&是要轉(zhuǎn)義的,記得前面有人提問(wèn),在導(dǎo)航 那一節(jié)中,在XAML中設(shè)置導(dǎo)航頁(yè)面/myPage.xaml?t1=aaaa&t2=bbbb,時(shí)會(huì)報(bào)錯(cuò),要知道XAML其實(shí)就是XML擴(kuò)展而 來(lái)的,特殊字符記住要轉(zhuǎn)義。
例如,我要發(fā)一條Toast通知,標(biāo)題為“你好”,內(nèi)容為“想請(qǐng)你吃飯”,參數(shù)為“/MainPage.xmal”,那么,我們POST的XML文檔應(yīng)當(dāng)為:
- <?xml version="1.0" encoding="utf-8" ?>
- <wp:Notification xmlns:wp="WPNotification">
- <wp:Toast>
- <wp:Text1>你好</wp:Text1>
- <wp:Text2>想請(qǐng)你吃飯</wp:Text2>
- <wp:Param>/MainPage.xaml</wp:Param>
- </wp:Toast>
- </wp:Notification>
知道這一點(diǎn)就好辦了,下面我們來(lái)做一個(gè)發(fā)送Toast消息的服務(wù)器端。
1、任你喜歡用哪個(gè)版本的VS,新建一個(gè)Windows應(yīng)用程序,很熟悉了吧,就是WinForm。
2、接著是界面,暈了,上傳不了圖片。這樣吧,你隨便扔幾個(gè)TextBox上去,分別用來(lái)填RUI,第一個(gè)值,第二個(gè)值,參數(shù),響應(yīng)消息。總共5個(gè),最后一個(gè)用來(lái)顯示發(fā)送結(jié)果,內(nèi)容較多,建議用多行。再放一個(gè)按鈕,觸發(fā)它的Click事件,點(diǎn)擊后立即發(fā)送。
好,我直接把所有代碼帖上,這東西不好講解,但相信你如果基礎(chǔ)學(xué)得好,肯定看得懂。
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Windows.Forms;
- using System.Net;
- using System.IO;
- namespace SendToast
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
- private void btnSend_Click(object sender, EventArgs e)
- {
- HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(txtUrl.Text);
- myRequest.ContentType = "text/xml";
- myRequest.Headers.Add("X-WindowsPhone-Target", "toast");
- /*
- * X-NotificationClass 處理間隔
- * 2 - 立即發(fā)送
- * 12 - 450秒內(nèi)發(fā)送
- * 22 - 900秒內(nèi)發(fā)送
- */
- myRequest.Headers.Add("X-NotificationClass", "2");
- // 要發(fā)送的內(nèi)容
- string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
- "<wp:Notification xmlns:wp=\"WPNotification\">" +
- "<wp:Toast>" +
- "<wp:Text1>" + txtValue1.Text + "</wp:Text1>" +
- "<wp:Text2>" + txtValue2.Text + "</wp:Text2>" +
- "<wp:Param>" + txtParam.Text + "</wp:Param>" +
- "</wp:Toast>" +
- "</wp:Notification>";
- byte[] buffer = Encoding.UTF8.GetBytes(toastMessage);
- myRequest.ContentLength = buffer.Length;
- myRequest.Method = "POST";
- using (Stream stream = myRequest.GetRequestStream())
- {
- stream.Write(buffer, 0, buffer.Length);
- }
- // 接收回應(yīng)
- HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
- string headers= "";
- foreach (var hd in myResponse.Headers.AllKeys)
- {
- headers += hd + " : " + myResponse.Headers[hd] + " | ";
- }
- headers += "\r\n";
- string msg = "";
- using (Stream recStream = myResponse.GetResponseStream())
- {
- StreamReader reader = new StreamReader(recStream, Encoding.UTF8);
- msg = reader.ReadToEnd();
- reader.Close();
- }
- msg += "\r\n\r\n";
- txtResult.AppendText(headers + msg);
- }
- }
- }
接下來(lái),到WP客戶端,同樣隨便你用什么版本的VS,新建一個(gè)Silverlight for Windows Phone應(yīng)用程序,有些人腦子比較敏感,看到Silverlight字樣不知發(fā)生什么事。其實(shí),只是了解它的人不多而已,Silverlight其實(shí)有 很多優(yōu)點(diǎn)的,慢慢體會(huì)吧,用客觀公正的視角去體會(huì)吧。
界面布局就好辦了,我直接上XAML,如果你看不懂,回去復(fù)習(xí)WPF。
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <TextBlock Name="txtInfo" TextWrapping="Wrap"/>
- </Grid>
后臺(tái)代碼也照帖了。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using Microsoft.Phone.Controls;
- using Microsoft.Phone.Notification;
- namespace WPApp
- {
- public partial class MainPage : PhoneApplicationPage
- {
- // 構(gòu)造函數(shù)
- public MainPage()
- {
- HttpNotificationChannel myChannel = null;
- // 推送信道的名字,隨便取一個(gè)就行了
- string ChannelName = "ToastChannel";
- InitializeComponent();
- // Find靜態(tài)方法可以根據(jù)名字查找信道
- myChannel = HttpNotificationChannel.Find(ChannelName);
- // 如果找不到,就要?jiǎng)?chuàng)建一個(gè)了
- if (myChannel == null)
- {
- myChannel = new HttpNotificationChannel(ChannelName);
- // 注冊(cè)事件
- myChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(myChannel_ChannelUriUpdated);
- myChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(myChannel_ErrorOccurred);
- myChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(myChannel_ShellToastNotificationReceived);
- // 打開(kāi)信道
- myChannel.Open();
- // 綁定Toast通知,這樣在程序不在前臺(tái)時(shí)才會(huì)顯示
- // 屏幕上方的通知提示條
- myChannel.BindToShellToast();
- }
- else
- {
- // 如果存在,還要注冊(cè)一次事件,因?yàn)樵诔绦虮蝗拥胶笈_(tái)后可能會(huì)刪除事件綁定
- myChannel.ChannelUriUpdated+=new EventHandler<NotificationChannelUriEventArgs>(myChannel_ChannelUriUpdated);
- myChannel.ErrorOccurred+=new EventHandler<NotificationChannelErrorEventArgs>(myChannel_ErrorOccurred);
- myChannel.ShellToastNotificationReceived+=new EventHandler<NotificationEventArgs>(myChannel_ShellToastNotificationReceived);
- // 在“輸出”窗輸出URL,因?yàn)槲覀冎皇菧y(cè)試,這樣一來(lái)方便一點(diǎn)
- System.Diagnostics.Debug.WriteLine("通道URI為:{0}", myChannel.ChannelUri.ToString());
- }
- }
- void myChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
- {
- string msg = "";
- foreach (string key in e.Collection.Keys)
- {
- msg += key + " : " + e.Collection[key] + "\r\n";
- }
- Dispatcher.BeginInvoke(() =>
- {
- this.txtInfo.Text = msg;
- });
- }
- void myChannel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
- {
- Dispatcher.BeginInvoke(() => MessageBox.Show(e.Message));
- }
- void myChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
- {
- // 當(dāng)URL發(fā)生改變后,還要輸出一次
- // 保證我們得到的是最新版本的URI
- Dispatcher.BeginInvoke(() =>
- {
- System.Diagnostics.Debug.WriteLine("通道URI:{0}", e.ChannelUri.ToString());
- });
- }
- // 這個(gè)方法不用我多介紹了
- protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
- {
- base.OnNavigatedTo(e);
- if (NavigationContext.QueryString.ContainsKey("toastmsg"))
- {
- this.txtInfo.Text = NavigationContext.QueryString["toastmsg"];
- }
- }
- }
- }
好了,那么,如何測(cè)試呢,毫無(wú)疑問(wèn),兩個(gè)程序要同時(shí)運(yùn)行,從VS的“輸出”窗口中把RUI復(fù)制到發(fā)送程序?qū)?yīng)的文本框中,填好幾個(gè)參數(shù),如標(biāo)題正文等,然后,你回到WP模擬器,點(diǎn)擊“開(kāi)始”按鈕,讓應(yīng)用程序不在最前臺(tái)。
再回到服務(wù)器端,點(diǎn)擊發(fā)送按鈕,等一會(huì)兒,你在模擬器中會(huì)看到Toast提示條的出現(xiàn)了。
沒(méi)辦法上傳圖片,只能這樣了。
下面,總結(jié)一下,推送通知其實(shí)不難的,其本質(zhì)就是HTTP通信,而且三種方式有兩種是固定格式的,打開(kāi)MSDN的示例,照抄就行了,一樣的。
但要理解它不是那么容易,記住要多練,學(xué)編程沒(méi)什么捷徑,最快的捷徑就是動(dòng)手干活。你可能會(huì)問(wèn):你是怎么熟悉這些技術(shù)的?
那我告訴你吧,這幾個(gè)推送通知的代碼,我已經(jīng)寫了十幾二十遍了,你說(shuō)我會(huì)不理解嗎?不信你也寫上十遍看看。