通知那些事儿(二):深度剖析本地与远程通知

本文译自《Local and Remote Notifications in Depth》

不管是本地通知还是远程通知,它们最基本的目的都是使应用不在前台时,传递给用户一些信息——比如,一条消息或者即将开始的约会。而本地通知和远程通知最基本的区别也是很简单的:

  • 本地通知是由应用本身调度安排并且传送到同一台设备上的;
  • 远程通知,也就是推送通知,是由你的服务器发送给APNs服务器,然后再推送到你的设备上的通知。

本地通知和远程通知给用户一致的视觉体验

用户可以通过下列方式得到通知:

  • 提醒框或横幅
  • 应用角标
  • 伴随提醒框、横幅或角标的提示音

从用户的角度出发,本地和远程通知都表明了应用里有一些有趣的东西。

例如,假设有一个管理TODO列表的应用,在这个列表中的每一项都有一个时间,而且每个都必须完成。用户可以请求应用在TODO项对应时间提前提醒用户。为了完成这个功能,应用需要在设定时间安排本地通知。假设应用不是展示提示消息,而是通过显示角标数字和提示音来提醒用户。到了约定的时间,iOS播放了一段音频和在App图标右上角显示了一个数字,如下图所示:

用户听到提示音和看到了角标,就启动应用去看TODO项。用户可以控制设备上的特定应用如何来处理这些通知。他们也能够对特定应用选择关闭或者打开远程通知的类型。

本地通知和远程通知对于App来说就不一样

当你的应用大部分时间是在前台的,UIKit会直接分发本地通知和远程通知给你的App delegate对象,而不展示任何系统UI控件。UIKit对本地通知调用了application:didReceiveLocalNotification:,对远程通知调用application:didReceiveRemoteNotification:fetchCompletionHandler: 方法。根据你的应用的实际情况,如何利用Provider提供的通知字典。因为你的应用正在运行,你可以静态的处理你获取到的通知数据并更新界面让你的用户知道新的消息。

当你的App必须启动来接受通知,UIKit在launch option字典里包含了UIApplicationLaunchOptionsLocalNotificationKeyUIApplicationLaunchOptionsRemoteNotificationKey键对应的字典直接传递给App delegate的application:willFinishLaunchingWithOptions:方法和application:didFinishLaunchingWithOptions:。这些键的存在让你知道这里有正在等待处理的通知数据,并且在应用启动时提供你一个可以正确显示你界面的机会。在你的应用运行中,你不需要在上面方法中处理通知,UIKit会调用App delegate其他方法,比如application:didReceiveLocalNotification:来给你处理通知数据的机会。
具体调用哪种方法,取决于你需要根据消息如何处理你的用户界面。

当你的应用正在运行,但又不是长时间运行在前台的,UIKit在后台尽可能地显示系统的UI并且将通知分发的结果告知你的应用。与正在运行的情况类似,UIKit会调用App delegate方法来接受通知数据并且处理任何操作。如果不在后台而不能分发通知数据,UIKit就会等到下次你App启动后分发该通知。

更多关于如何处理通知,可以参考注册、调度以及处理用户通知

更多关于本地通知的小道消息

本地通知对于基于时间行为的应用是非常适用的,日历或者ToDo列表这样的应用。在iOS系统允许应用后台运行的有限时间内,你也会发现本地通知很有用。例如,那些在后台依靠不断轮询服务器来获取消息和数据的应用。如果消息已经可以查看或者一个更新已经可以下载了,它们就能根据需要处理的数据,以恰当的方式通知用户。

本地通知是UILocalNotification或者NSUserNotification的一个实例,有三个基本属性:

  • Scheduled time:你必须指定系统分发通知的日期与时间,这就是fire date。你可以限制触发通知的日期的时区以适应你在旅行,也可以定期触发通知(每周、每月等);
  • Notification type:这些属性包括提醒消息,标题和默认的动作按钮,应用角标的数字,提示音以及在iOS 8之后可选的自定义动作;
  • Custom data:本地通知可以包含一个自定义数据的字典。

每个应用限定只能触发64个本地通知。超过这个限制,系统就会忽略旧通知,而保持最新的通知。循环通知被看做是一个通知。

更多关于远程通知的小道消息

一个iOS或者Mac应用大部分是一个基于客户端/服务器(clent/server)模式的程序。客户端是安装在设备或电脑上的应用;服务器端主要的功能为客户端提供数据,因此术语叫做Provider。客户端定期的连接到服务器下载数据。Email和社交应用就是客户端/服务器模式的实例。

但是假如应用没有连接到服务器或者甚至没有在设备或电脑上运行,但是服务器又有新数据供应用下载要如何处理?应用要怎么了解有等待下载的数据?远程(或者推送)通知就是摆脱这种困境的方法。一个远程通知就是就是Provider传递给设备或电脑操作系统的一条短消息。而操作系统就会依次通知客户端的用户有新数据要下载,或者有新消息需要查看等。如果用户开启了这个特性并且应用正确的注册通知,通知就会被送达到操作系统,然后尽可能地分发给应用。APNs是远程通知特性的主要技术。

对于桌面系统的运行在后台的应用,远程通知服务器提供了类似的服务,但是没有附加的多次分发。对于当前没有正在运行,或在iOS系统中没有在前台运行的应用,通知就间接分发。操作系统代替应用接受到远程通知,然后提醒用户。如果用户启动了应用,就会从服务器下载数据。如果当通知来的时候应用正在运行,应用就会选择直接处理通知。

就像名字所表明的含义一样,Apple Push Notification service利用远程设计来给设备或电脑分发远程通知。推送设计(push design)有别于它的反面——拉取设计(poll design)。因为通知的接受者是被动地监听和更新而不是主动地轮询服务器。推送设计使得大规模及时地传播变成了可能,而且又极大避免拉取设计中可扩展的问题。APNs利用一个持久的的IP连接来实现远程推送。

大多数远程通知由payload组成,payload是个JSON字典,里面包含了APNs定义的用户如何被提醒的属性。payload越小,你的通知性能就越好。因此,你可以自定义属性,而不是使用远程通知机制来进行数据传输。分发是尽最大努力交付而不保证一定会成功。更多关于payload,可以参考The Remote Notification Payload

当你的设备不在线上,APNs就会保留从服务器获得并分发给设备上应用的最后一条通知。当设备上线后,APNs就会将所存储的通知推送给设备。运行iOS设备的可以通过Wi-Fi和移动数据接受远程通知。运行OS X的电脑则可以通过Wi-Fi和以太网来接受。

注意:在iOS中,远程通知会在Wi-Fi打开并且连接的情况下优先使用Wi-Fi网络。如果Wi-Fi连接失败,远程通知就会尝试使用设备的移动数据连接。

对于大多数通过Wi-Fi接受远程通知的设备来说,设备屏幕必须开启(不能睡眠)或者必须连上电源。iPad即使在睡眠的时候也会保持Wi-Fi连接,允许远程通知的分发。iPad Wi-Fi无线电端口会处理需要的进入的通信数据。

频繁的发送通知会影响电池使用寿命,因为设备需要连接网络来接受通知。