重新想象 Windows 8 Store Apps (67) - 后台任务: 推送通知

原文: 重新想象 Windows 8 Store Apps (67) - 后台任务: 推送通知

[源码下载]


重新想象 Windows 8 Store Apps (67) - 后台任务: 推送通知



作者:webabcd


介绍
重新想象 Windows 8 Store Apps 之 后台任务

  • 推送通知



示例
1、客户端
BackgroundTask/PushNotification.xaml

<Page

    x:Class="XamlDemo.BackgroundTask.PushNotification"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="using:XamlDemo.BackgroundTask"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d">



    <Grid Background="Transparent">

        <StackPanel Margin="120 0 0 0">

            

            <TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" />

            

            <Button Name="btnCreateChannel" Content="create the channel" Margin="0 10 0 0" Click="btnCreateChannel_Click" />

            

            <TextBox Name="txtUri" Margin="0 10 10 0" />

            

            <Image Source="wns.png" Margin="0 10 0 0" HorizontalAlignment="Left" Width="800" />

            

        </StackPanel>

    </Grid>

</Page>

BackgroundTask/PushNotification.xaml.cs

/*

 * 演示如何接收推送通知

 * 

 * 注:

 * 需要在 Package.appxmanifest 中增加后台任务声明,并勾选“推送通知”

 * 在 win8 商店创建了应用后,需要将此 app 的商店中的 identity 复制到 Package.appxmanifest 的 identity 节点

 * 不能在模拟器中运行

 * 每次新建的 channel 有效期为 30 天

 * 

 * 另:

 * WNS - Windows Push Notification Service

 * 推送通知的服务端参见:WebServer/PushNotification 内的文件

 */



using System;

using Windows.ApplicationModel.Background;

using Windows.Networking.PushNotifications;

using Windows.UI.Notifications;

using Windows.UI.Popups;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;



namespace XamlDemo.BackgroundTask

{

    public sealed partial class PushNotification : Page

    {

        public PushNotification()

        {

            this.InitializeComponent();

        }



        private async void btnCreateChannel_Click(object sender, RoutedEventArgs e)

        {

            // 当收到推送的 raw 通知时,如果 app 在锁屏,则可以触发后台任务以执行相关的逻辑(PushNotificationTrigger)

            BackgroundAccessStatus status = BackgroundExecutionManager.GetAccessStatus();

            if (status == BackgroundAccessStatus.Unspecified)

            {

                status = await BackgroundExecutionManager.RequestAccessAsync();

            }

            if (status == BackgroundAccessStatus.Denied)

            {

                await new MessageDialog("请先将此 app 添加到锁屏").ShowAsync();

                return;

            }



            // 创建一个推送通知信道,每个新建的 channel 有效期为 30 天,所以建议每次进入 app 后都重新建一个 channel(但是需要注意间隔较短的话,则会复用之前的 channel)

            PushNotificationChannel channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

            // 接收到通知后所触发的事件

            channel.PushNotificationReceived += channel_PushNotificationReceived;



            // channel.Close(); // 关闭 channel

            // channel.ExpirationTime; // channel 的过期时间,此时间过后 channel 则失效



            // channel 的 uri 地址,服务端通过此 uri 向此 app 推送通知

            txtUri.Text = channel.Uri.ToString();

        }



        void channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)

        {

            switch (args.NotificationType)

            {

                case PushNotificationType.Badge: // badge 通知

                    BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(args.BadgeNotification);

                    break;

                case PushNotificationType.Raw: // raw 通知

                    // 当收到推送的 raw 通知时,如果 app 在锁屏,则可以触发后台任务以执行相关的逻辑(PushNotificationTrigger)

                    string msg = args.RawNotification.Content;

                    break;

                case PushNotificationType.Tile: // tile 通知

                    TileUpdateManager.CreateTileUpdaterForApplication().Update(args.TileNotification);

                    break;

                case PushNotificationType.Toast: // toast 通知

                    ToastNotificationManager.CreateToastNotifier().Show(args.ToastNotification);

                    break;

                default:

                    break;

            }



            args.Cancel = true;            

        }

    }

}

Package.appxmanifest

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

<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">

  <!--以下借用“贪吃的蛇”的 Identity,以便演示推送通知-->

  <!--Identity Name="10437webabcd.173815756DD78" Publisher="CN=27514DEC-C708-4EDB-BFEA-F956384483D0" Version="1.0.0.0" /-->

  <Identity Name="webabcd.win8.XamlDemo" Publisher="CN=wanglei" Version="1.0.0.0" />

</Package>


2、服务端
WebServer/PushNotification/OAuthToken.cs

/*

 * 用于反序列化从 https://login.live.com/accesstoken.srf 获取到的结果

 */



using System.Runtime.Serialization;



namespace WebServer.PushNotification

{

    [DataContract]

    public class OAuthToken

    {

        [DataMember(Name = "access_token")]

        public string AccessToken { get; set; }

        [DataMember(Name = "token_type")]

        public string TokenType { get; set; }

    }

}

WebServer/PushNotification/OAuthHelper.cs

/*

 * https://login.live.com/accesstoken.srf 的 OAuth 验证的帮助类

 */



using System;

using System.IO;

using System.Net;

using System.Runtime.Serialization.Json;

using System.Text;



namespace WebServer.PushNotification

{

    public class OAuthHelper

    {

        /// <summary>

        /// 获取 https://login.live.com/accesstoken.srf 的 OAuth 验证的 access-token

        /// </summary>

        /// <param name="secret">在 win8 商店创建 app 后获取到的 “客户端密钥”</param>

        /// <param name="sid">在 win8 商店创建 app 后获取到的 “程序包安全标识符(SID)”</param>

        /// <returns></returns>

        public OAuthToken GetAccessToken(string secret, string sid) 

        {

            var urlEncodedSecret = UrlEncode(secret);

            var urlEncodedSid = UrlEncode(sid);

            var body = String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com",

                                     urlEncodedSid,

                                     urlEncodedSecret);



            string response;

            using (WebClient client = new WebClient())

            {

                client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

                response = client.UploadString("https://login.live.com/accesstoken.srf", body);

            }

            return GetOAuthTokenFromJson(response);

        }



        private OAuthToken GetOAuthTokenFromJson(string jsonString) 

        {

            using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))

            {

                var ser = new DataContractJsonSerializer(typeof(OAuthToken));

                var oAuthToken = (OAuthToken)ser.ReadObject(ms);

                return oAuthToken;

            }

        }



        private static string UrlEncode(string str)

        {

            StringBuilder sb = new StringBuilder();

            byte[] byStr = System.Text.Encoding.UTF8.GetBytes(str);

            for (int i = 0; i < byStr.Length; i++)

            {

                sb.Append(@"%" + Convert.ToString(byStr[i], 16));

            }



            return (sb.ToString());

        }

    }

}

WebServer/PushNotification/Push.aspx.cs

/*

 * 演示如何向 app 推送通知

 * 

 * 注:

 * 关于推送通知服务请求和响应头的详细说明参见:http://msdn.microsoft.com/zh-cn/library/windows/apps/hh465435.aspx

 */



using System;

using System.IO;

using System.Net;

using System.Text;



namespace WebServer.PushNotification

{

    public partial class Push : System.Web.UI.Page

    {

        protected void Page_Load(object sender, EventArgs e)

        {

            // 向某个 app 推送通知的 channel 地址

            string notifyUrl = "https://sin.notify.windows.com/?token=AgYAAABQnraWSMQXRveiofxSXGKMHaPB84FLMhFa3D6TQZRHzRSPeByl%2f1O%2frPAcc3ipjpT2cQXfivh589zEV8AOYkR%2fLwXoT2esZnC3hS%2fN7q94ZzJFLnpQsDsYNolFiEAhbHQ%3d";



            // 在 win8 商店创建 app 后获取到的 “程序包安全标识符(SID)”

            string sid = "ms-app://s-1-15-2-1530173461-470787880-3155417234-2904423500-2475821181-4070965884-3773336209";

            // 在 win8 商店创建 app 后获取到的 “客户端密钥”

            string secret = "bs08Acs1RG7jB7pkGVMh8EmGKCG3pH+3";



            OAuthHelper oAuth = new OAuthHelper();

            OAuthToken token = oAuth.GetAccessToken(secret, sid);



            try

            {

                HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(notifyUrl);



                // toast, tile, badge 为 text/xml; raw 为 application/octet-stream

                myRequest.ContentType = "text/xml";

                // 推送消息的类型:wns/toast | wns/badge | wns/tile | wns/raw

                myRequest.Headers.Add("X-WNS-Type", "wns/toast");

                // 设置 access-token

                myRequest.Headers.Add("Authorization", String.Format("Bearer {0}", token.AccessToken));



                // 注:app 通过 toast 激活后可以通过 OnLaunched() 中的 args.Arguments 来获取此处 launch 指定的值

                string toastMessage = "<toast launch='param'><visual version='1'><binding template='ToastText01'><text id='1'>推送通知:" + DateTime.Now.ToString("mm:ss") + "</text></binding></visual></toast>";

                byte[] buffer = Encoding.UTF8.GetBytes(toastMessage);



                myRequest.ContentLength = buffer.Length;

                myRequest.Method = "POST";



                using (Stream stream = myRequest.GetRequestStream())

                {

                    stream.Write(buffer, 0, buffer.Length);

                }



                using (HttpWebResponse webResponse = (HttpWebResponse)myRequest.GetResponse())

                {

                    /*

                     * 响应代码说明

                     *     200 - OK,WNS 已接收到通知

                     *     400 - 错误的请求

                     *     401 - 未授权,token 可能无效

                     *     403 - 已禁止,manifest 中的 identity 可能不对

                     *     404 - 未找到

                     *     405 - 方法不允许

                     *     406 - 无法接受

                     *     410 - 不存在,信道不存在或过期

                     *     413 - 请求实体太大,限制为 5000 字节

                     *     500 - 内部服务器错误

                     *     503 - 服务不可用

                     */

                    Response.Write(webResponse.StatusCode.ToString()); //如果成功应该是返回 200 ok

                }

            }

            catch (Exception ex)

            {

                Response.Write(ex.ToString());

            }

        }

    }

}



OK
[源码下载]

你可能感兴趣的:(windows)