2016 年 4 月

第 31 卷,第 4 期

领先技术 - 向 Mobile Apps 推送通知

作者 Dino Esposito | 2016 年 4 月

Dino Esposito在我的前几篇专栏中,主要是关于软件设计和体系结构的内容。尽管体系结构在建立软件系统时所扮演的角色不容小觑或不容否定,但任何应用程序最终都是各个功能之和。这和用户体验驱动开发 (UXDD) 理念同理,这种理念认为衡量某种软件应用程序是否成功的标准就隐藏在用户操作应用程序的体验之中。如果您的软件系统包含移动前端,那么您几乎无法忽视类似推送通知这样的功能。

在这篇专栏中,我将对无论使用哪种移动操作系统,在其顶端添加通知层都需要具备的必要条件进行总结。在这种情况下,我将回顾 Microsoft Azure 通知中心平台的服务。

推送通知概览

推送通知是在没有先发送明确请求的情况下,移动设备从应用程序后端接收到的消息。应用程序的客户端和服务器组件之间的大部分交互通过明确的操作实现,通常是一种征求反馈的用户操作。从某种程度上说,推送通知是一种未经请求的反馈,是在出现某些可用的重要信息时由后端发出的消息。确切地讲,“未经请求的”一词用在此处并不十分准确。任何一种移动应用程序都必须订阅可用源才能接收推送通知。然而,一旦订阅了服务,用户会接收到未经请求的通知。

无论用户是否积极使用该应用,推送通知对用户的实际影响都在于相关更新的发送。例如,安装飞机航班的应用程序会让您快速并及时接收有关航班和登机门更改的更新信息。在某一时刻,您的手机铃声响起或产生震动,手机屏幕上的某处显示出可见的反馈。然而,反馈的显示位置将完全取决于您的设备所基于的操作系统版本、应用配置和用户设置。根据操作系统的不同,推送通知将以上栏图标、Toast 消息、锁屏提醒更新或其他形式出现。

推送通知的关键在于其所有的技术环节都是严格特定于平台的。例如,在 iOS、Android、Windows Phone 和通用 Windows 平台 (UWP) 应用中,应用程序订阅推送通知服务的方式各不相同。同样地,各平台使用自身的有效负载向连接的设备发送消息,各平台要求您配置不同的分派引擎。尽管与实际执行会有明显出入,我相信推送通知系统 (PNS) 的整体体系结构相当普遍,类似图 1 中的图表。

平台特定推送通知系统的整体体系结构
图 1 平台特定推送通知系统的整体体系结构

与多个推送通知系统协同工作

出现多个移动平台也反映出多个推送通知平台的存在也就不足为奇。创建多平台移动应用的开发者必定熟悉多个不同推送通知引擎且必须为各引擎建立相应的服务器环境。除非您在开发一个针对单个移动平台的应用(没有要将其移植到其他平台的可预见的计划),否则您可能会想了解跨平台 PNS,如图 2 所示。与图 1 中的图表相比,新的体系结构增添了一个通知层并为提供了单个入口点以进行编程。

跨平台推送通知系统的整体体系结构
图 2 跨平台推送通知系统的整体体系结构

移动应用程序注册泛型推送通知中心,应用程序的后端排列向此中心发送的消息队列。接下来,此中心将使用相应的协议和有效负载发送实体消息到指定的移动平台。借助其中某个跨平台移动通知系统,只需单击即可使用单个 API 向 Android、iOS 和 Windows Phone 设备推送消息。

Azure 通知中心至是众多跨平台通知系统中的一个。让我们看看它是如何工作的。

Azure 通知中心

使用 Azure 通知中心的第一步是设置 Azure 计划。此服务分为三种级别,其中最低级别免费且设定了可访问的设备数量限制(500 个),以及您最多可以发送的通知条数(1 百万条)。有关详细信息,请参阅 bit.ly/1Tbimew。您只需拥有一个免费帐户用来创建通知中心并在其中创建命名空间即可马上开始使用。此信息会在之后建立与应用程序后端的连接以排列设备消息队列时派上用场。

使用推送通知时最难处理的环节并不是处理跨平台中心,而是满足您想要访问各移动平台的要求。例如,为了向 iOS 或 Android 设备发送更新,您必须先使用各自的本机 PNS 对您的应用程序进行完全配置。有关详细信息,请访问 bit.ly/1o15uv2。您可能也知道,只有注册过的 iOS 应用才能接收推送通知。若要使用本机 Apple Push Notification 服务注册应用,您必须先从 Apple 获取证书,该证书有助于仅识别来自您应用的通知。对于 Android 来说,则要求您转为通过 Android Studio 界面获取 API 密钥。对于 UWP 应用,您必须先注册 Windows 应用商店。您必须为您打算支持的任何平台完成所有平台特定步骤。您必须将您获得的任何注册信息输入 Azure 通知中心,这些注册信息将代表您在本机 PNS 方面的操作。

使用此中心注册该应用程序

假设您可从 Apple 系统接收通知的 iOS 应用程序目前拥有一份 12 页的证书文件和更新的预配配置文件。即使您未曾使用过 Azure 通知中心,您也几乎可以完成相关配置。您确定要使用中级中心系统吗?

需要指出的是,任何平台特定 PNS 仍然剩下大量工作留给开发人员以执行常见的请求任务,例如,向所有连接设备进行普通播送,或向在特定区域设置使用设备等的特定用户群组进行普通播送。尤其是播送,由于它可能会在设备数量增加时提出不常见的可伸缩性问题,因而不属于日常任务。这就是中间的可缩放基础结构从您编写的代码中分离出核心平台特定服务会带来可观好处的原因。然而,若要使用 Azure 通知中心,您还需要上载 12 页的 Apple 证书,并以编程方式让您的应用程序注册 Azure 中心。如果您正在使用 Xamarin iOS 编写 iOS 应用,请前往 bit.ly/1KaySJ3 查看出色的分步教程。

该移动应用程序身负两大主要职责。第一点是,需要合并订阅 Azure 通知源的代码。第二点是,需要包含代码以采用某些方式处理即将收到的任何通知。一个 iOS 应用实际从 Apple 服务接收推送通知,因而需要订阅此服务。这可以通过 UIApplication.Shared­Application.RegisterForRemoteNotifications 方法完成。一旦退出,此方法将在名为 RegisteredForRemoteNotifications 的应用委托类中调用一种可重写的方法。您可在此处订阅 Azure 中心。此方法从操作系统接收设备令牌且您的代码只需将其转发到中心。Azure 中心由路径和连接字符串识别,就像:

var hub = new SBNotificationHub(connectionString, hubPath);
hub.RegisterNativeAsync(deviceToken, null);

接下来,当实际收到通知后,在应用委托类中调用另一种可重写的方法: ReceivedRemote­Notification。此方法以(可能为嵌套的)字符串字典形式从操作系统接收推送消息的实际内容。此重写方法通过某种更合适的方式(锁屏提醒、声音或警报)对提取并显示实际信息负责。

排列发送到 Azure 中心的消息队列

目前仅仅付诸了一半努力,就完成了全部的工作。剩下的问题就是,了解如何发送消息到 Azure 中心再将其从 Azure 中心发送到连接设备。换言之,您需要拥有了解中心连接详情并为用户传递消息的应用程序后端。这样的应用程序后端可以是任何类型的 .NET 应用程序,包括 ASP.NET 应用程序。如果您出于业务原因使用移动应用接收推送通知,业务域中的某些功能将生成相关消息。它可以是用来发送消息的软件触发器或管理用户的操作,如图 3 所示。

通过 Azure 中心将区域设置特定消息发送到 iOS 和 Android 应用的 ASP.NET 后端
图 3 通过 Azure 中心将区域设置特定消息发送到 iOS 和 Android 应用的 ASP.NET 后端

若要将推送通知合并到 ASP.NET 后端,您只需要 Microsoft Azure NotificationHubs Nuget 包。此外,您的代码要负责构建合适的连接字符串。连接字符串包含与 Azure 服务总线 URL 终结点和加密令牌的相关信息。服务总线终结点包含您设置 Azure 服务时创建的命名空间名称。类似于 sb://your-ns.servicebus.windows.net。在标签“连接字符串”下方命名空间的确认页面读取您的加密令牌。 创建有效中心实例所需的代码如下:

var hub = NotificationHubClient.CreateClientFromConnectionString(
  connString, hubName);

下一步包含您打算为各目标平台创建合适的有效负载。有效负载是一种遵循固定模式的 JSON 字符串。您可以按照您想要的方式生成 JSON 字符串。在以下示例中,$ 是适用于发送实际消息的占位符:

const string iosFormat = "{\"aps\":{\"alert\":\"$\"}}";
const string androidFormat = "{\"data\":{\"message\":\"$\"}}";
var iosAlert = iosFormat.Replace("$", actualMessage);
var androidAlert = androidFormat.Replace("$", actualMessage);

一旦完成有效负载的构建,将其发送到中心会变得和执行以下代码一样简单:

var task1 = hub.SendAppleNativeNotificationAsync(iosAlert);
var outcome1 = task1.Result;
var task2 = hub.SendGcmNativeNotificationAsync(androidAlert);
var outcome2 = task2.Result;

代码片段中的结果变量为 NotificationOutcome 类型实例并返回关于运行结果的详细信息。

发送基于模板的消息

之前的代码示例只显示了一种最简单的发送推送通知的方式—始终不变的向任何连接设备广播普通的文本字符串。而且,您还需针对关注的各移动平台将其格式化。更常见的方案是发送基于模板的消息。基于模板的消息以字符串字典的形式发送到 Azure,且 Azure 中心确保其可连接到此帐户已配置的任何移动平台。基于模板的消息背后的关键理念是该应用程序计划使用比默认情况更丰富的形式。例如,让我们看一看如何按照以下不同区域设置向用户发送不同消息:

var locale = "EN";
var template = String.Format("{{\"aps\":{{\"alert\":\"$(News_{0})\"}}}}", locale);

此示例显示一个模板,以通过 Apple PNS 注册任何入站的基于模板的名为 News_XX 的消息,其中 XX 为区域设置的开头两个字母。此处模板的一个优点在于应用程序后端可能会以单个字典形式发送多个条目,但每个设备只能收到其注册过的消息。这只是 Azure 通知中心这样的中级中心带来的其他服务之一。若要发送区域设置特定消息,您需要以下代码:

var messages = new Dictionary<string, string>
{
  {"News_EN", "..."},
  {"News_ES", "..."},  {"News_PT", "..."},
  {"News_IT", "..."}
};var task = hub.SendTemplateNotificationAsync(messages);
var outcome = task.Result;

仅仅一个推送,就可以发送到跨平台的各个设备,可保证每位用户只看到与他在手机上选定的区域设置相应的通知。请注意,这是一项与提供的消息自动区域设置略微不同的功能,例如在 iOS 设备上。实际上,在 iOS 设备上,您可使用在本地化的字符串字典中映射到条目的占位符发送消息,且此 iOS 在调用警报前奇迹般地实现了消息的自动转换。而模板消息是一种 Azure 功能,允许您向分段的用户群组发送不同的消息,将由您决定如何为用户群组分段。

计划的消息

计划的消息会是您在 Azure 通知中心发现的另一个有趣的功能。计划的消息是发送到 Azure 的通知,但只在规定时间内发送到连接设备。若要发送计划通知,您只需采用略微不同的 API:

var notification = new TemplateNotification(messages);
var task = hub.ScheduleNotificationAsync(notification, new DateTime(...));
var outcome = task.Result;

值得注意的是,计划通知要求标准层订阅且不适用于免费测试订阅。

成为一名良好公民

撇开如何通过 Azure 中心注册和发送推送通知的技术环节,推送通知的难点在于如何使用它们与用户建立成功交流。

您一定不希望整天都是乏味的信息式消息令用户感到厌烦。因为这是“推送”通知,您要确保用户对接收推送真的感兴趣。在这点上,未分段的用户群组是可依赖的重要功能。消息长度也很重要。我建议您保持和 tweet 类似的消息长度。例如,截至到 iOS 8,在更新的系统中,推送通知的最大长度由原来的 256 字节扩展到 2 千字节。Android 中则为 4 千字节。最后一点也非常重要,确保可以让您的目标操作系统轻松退出整个功能。对于最近发布的操作系统来说基本上是这样,但您最好选择双击。


Dino Esposito是《Microsoft .NET: 构建面向企业的应用程序》(Microsoft Press,2014 年)和《使用 ASP.NET 构建新型 Web 应用程序》(Microsoft Press,2016 年)的作者。作为 JetBrains 的 .NET 和 Android 平台的技术推广人员,Esposito 经常在全球行业活动中发表演讲,并在 software2cents@wordpress.com 上以及 Twitter @despos 上的推文中分享他对于软件的愿景。

衷心感谢以下 Microsoft 技术专家对本文的审阅: Jon Arne Saeteras