基本功

iOS在诞生之初为了最大程度的保证用户体验,做了一些高瞻远瞩且影响深远的设计。APNs(Apple Push Notification service)就是其中一项。

早期iOS设备的内存和CPU资源都很有限,为了让前台活跃的app拥有尽可能多的系统资源,以及节约设备电量,iOS一开始就“不允许”普通app的进程常驻后台。这个决定很大程度上保障了用户体验和延长了手机的待机时间,但app的开发商需要和他们的用户保持联系。开发商需要有一个稳定的网络通道能每隔一段时间推送新的内容到用户设备。Apple决定自己来搭建维护这个通道,也就是我们今天所说的APNs。记得刚开始接触iOS开发的时候,看到有不少开发者吐槽push机制,觉得不可控且增加了开发成本。其实稍微思考下Apple今天的平台规模和消息量,以及所带来的成本消耗,就能明白Apple设计这个机制所需要的智慧和魄力。一切都是为了用户。

APNs虽然允许开发商推送消息到用户设备,但考虑到消息的量级和成本,这个由Apple维护的长链接通道就不可能是无限制使用的。APNs有着诸多的限制:

可靠性。一般情况下,Apple会保证这个通道的Qaulity of Service,也就是推送的消息能及时稳定到达设备。不过一旦用户的设备处于offline状态,Apple只会存储发送给用户的最新一条push,之前发送的push会被直接丢掉。而且这最后一条离线push也是有过期时间的。一些用户应该有过这种经历,在使用微信的时候,明明对方发送了多条消息,却只收到了一条push。

Payload Size。每一条push消息的包体大小是有最大限制的。Apple在文档里清楚的说明,push只应该用来通知用户有新的内容,而不应该用来承载内容本身。理论上payload size越小,push到达设备的概率就越高。在iOS8之前max payload size是256字节,到iOS8发布这个最大值被调整到了2048字节,再到最近的iOS9发布,引入了HTTP2.0,payload size又被设为4KB了。老版本的256字节实在有点捉襟见肘,连塞一个链接进去都要考虑再三。到2KB的时候就宽裕多了,已经有不少开发商开始尝试往里面放少量的业务数据了,如果能减少打开app之后的一次网络请求何乐而不为呢。当然4KB的想象空间会更大。Apple一直在调整这个数值,为的是给开发商更多的空间去提升用户体验。push慢慢变的不仅仅是一条“alert”那么简单了。

成功率并不高。Apple虽然保证了push通道一定程度的可靠性,但push由于各种各样的原因并不能保证较高水平的到达率。push需要向用户申请权限,即使当时赋予了权限,后面也可能由于push过于频繁被用户又关掉。在夜间模式下push虽然能到达通知栏,可用户没有任何感知,更不用说点击push启动app了。还有server端token失效,这点可以通过feedback service来清理失效的token。Apple的APNs server据说每天会发送超过百亿条push,在某个时间段出现峰值的时候,开发商server和Apple server连接的成功率也会降低。还有客户端设备所处网络环境并不稳定等等因素,使得通过push成功启动app的成功率并不怎么高。

理解了上面这些限制,就能按照Apple的规范向用户推送内容了。但push里面的门道远不止这么简单,Apple也从没有停止过对APNs体验的优化,类似payload size调整,interactive notification等等,每一个新的feature增加,哪怕是细微的改动,都能被聪明的开发者加以利用,以四两拨千斤提升产品的体验。下面就介绍一些笔者所了解到的“隐蔽门道”。

不仅仅是Local Push

很多个人开发者不具备搭建server的条件,一般会设置一个定时的local push来提醒用户唤醒自己的app。Local push看起来似乎是个廉价的折中方案,事实上它可以更强大。APNs(一般也叫做remote push)因为有上面的各种限制,并不能很好的契合业务需要。而Local Push则不同,拥有完整的app业务上下文,还可以对push进行定制化。如果可以用Local Push替代Remote Push对体验的提升是不言而喻的。Loca push的限制在于app必须处于运行状态才能发起,很多聪明的开发商会开启background task,在用户按了home键之后再争取到几分钟的运行时间,在这期间所有的remote push都被替换成了local push。不要小看了这几分钟的时间,对于很多活跃度高的app来说,按home键之后马上又产生新的用户内容的概率并不小。微信,WhatsApp都采用了这种机制来提升体验。

叫醒你的App

开启background task之后虽然能够再多运行一会,但时间一到,app还是会被挂起或者kill。大部分多时候你的app是处于非活跃状态。很多app都需要预先获取内容,或者后台下载文件等来减少用户的等待时间。iOS7引入的Silent Notification和Background Fetch机制可以一定程度上满足这种需要。silent push实现比较简单,开启相关后台权限之后发送如下特定格式的json就能启用。

唤醒app之后能处理的业务就多了,这对不少app来说是个非常实用的拓展,预加载内容也好,生成local push也好,都能提升体验。但这种唤醒机制并不总是可靠,有时候会“叫不醒”。app如果被手动kill叫不醒,如果background fetch被用户关闭也叫不醒,但这两种情况在手机充电的时候又可以被叫醒。Apple有一套自己的“智能”策略。

前台消息通道

大部分时候APNs都被用来通知用户某个处于background的app有新内容。但其实说白了APNs不过就是一条基于长链接的数据通道,在app处于foreground的时候也是能收到push消息的,不过不会有任何UI展示提醒而已。处理回调的位置也是在 也就是说APNs其实还是个免费的前台消息通道。而且有时候走APNs通道会比自己的server通道更快,如果客户端做好数据去重,多一个辅助的数据通道当然能提升体验。

新神通PushKit

APNs设计的初衷是避免app常驻后台,只在用户点收到push的时候主动去启动app。前面提到的silent push可以在有限的场景下,无需用户感知启动app。但到iOS8引入PushKit framework之后,app就可以通过push随时唤醒了,不过这个新的神通暂时还只限于voip类应用。

之前在社区看到有人提问,说微信电话本可以在用户挂掉电话的时候,把呼叫中的push改成未接电话,好奇是怎么办到的。因为大家都知道remote push是无法通过server动态修改push内容的,所以答案只有一个可能,app被后台唤醒了。用户看到的push其实是local push,而local push是可以在客户端随意调整的。唤醒到方式就是利用PushKit。

当然好处不仅仅是修改push内容这么简单。WhatsApp的用户在iOS8之后应该会有明显的感觉,好像很少看到启动页面了。看起来似乎是WhatsApp开启了voip后台常驻运行模式,但这种模式会比较费电,一些用户会有顾虑。真相也并非如此,WhatsApp并没有常驻后台,只不过是开启了PushKit的push唤醒机制。每次用户有新的离线消息,普通文本或者是voip call,app都会先被后台唤醒,再从server拉取离线消息,最后生成local push。等用户点击local push启动app的时候,没有启动页面,没有connecting和loading,所有的数据已经准备就绪,就好像WhatsApp一直在后台运行一样。也就是说,WhatsApp其实已经把所有的push都换成了local push。验证方法也很简单:

  • 手动kill WhatsApp。
  • 手机进入飞行模式。
  • 收一条离线消息。
  • 使用tcpdump开始监听iphone网络包,关闭飞行模式。
  • 这时候,app被push唤醒,能看到如下图一条WhatsApp相关的域名解析,说明app被启动了。而且能看到很多后续的服务器交互(拉取离线消息之类)。  2 微信不知道是出于什么考虑,既没有开启voip后台常驻模式,也没有利用PushKit唤醒机制。每次收到消息之后打开app,都是先看到地球,连接中,收取中,到真正看到最新消息经常需要3s以上。PushKit已经没有电量方面的额外损耗了,对voip类应用的体验提升非常之大。

具体怎么实现PushKit可以参照文章末尾的链接地址。

总结

关于push这条长链接通道,Apple几乎在每次的iOS新版本里都会增加一些feature。为了控制新feature带来的影响,每次改动都不多,但怎么利用这些feature就看开发者各自都功力了。对用户体验带来的改变远不止官方文档上介绍的那么简单,只有多思考,时刻关注行业最新动态,才能发掘更多的隐藏“门道”。

参考文章

  1. Apple APNs官方文档:https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html
  2. Silent Push实现:http://hayageek.com/ios-silent-push-notifications
  3. PushKit实现:https://zeropush.com/guide/guide-to-pushkit-and-voip

来自: http://music4kid.github.io/ios/2016/01/06/push/

感谢:http://www.open-open.com/lib/view/open1452487743355.html

IOS的APNS和PushKit门道详述的更多相关文章

  1. IOS 基于APNS消息推送原理与实现(JAVA后台)

    Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Pu ...

  2. 转:IOS 基于APNS消息推送原理与实现(JAVA后台)

    Push的原理: Push 的工作机制可以简单的概括为下图   图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...

  3. IOS 基于APNS消息推送原理与实现(JAVA后台)--转

    Push的原理: Push 的工作机制可以简单的概括为下图   图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...

  4. iOS 基于APNS消息推送原理与实现(包括JAVA后台代码)

    Push的原理: Push 的工作机制可以简单的概括为下图   图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...

  5. IOS本地,APNS远程推送(具体过程)

    添加本地推送 ///本地添加 -(void)addLocalPushNotification:(UIButton*)sender; { NSLog(@"%s",__FUNCTION ...

  6. Push:iOS基于APNS的消息推送

    1. Push的三个步骤,如下图所示: (1)Push服务应用程序把要发送的消息.目的iPhone的标识打包,发给APNS: (2)APNS在自身的已注册Push服务的iPhone列表中,查找有相应标 ...

  7. iOS之 APNs全新的APNs苹果15年WWDC大会上的干货

    记得14年在dl某大学校招上现场面试iOS时候被问到了APNs也就是苹果的推送问题,当时我表示一脸懵逼,因为当时还没有真正接触做过项目也就了解了个大概,总之当时回答的一塌糊涂!后来回去就在网上仔细查了 ...

  8. iOS 下APNS推送处理函数具体解释

    相比起Android,iOS在推送方面无疑惯例得更好.APNS(Apple Push Notification Service)是苹果公司提供的消息推送服务.其原理就是.第三方应用将要推送给用户的信息 ...

  9. IOS 基于APNS消息推(JAVA后台)

    直接上Demo import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUti ...

随机推荐

  1. 排序+逆向思维 ACdream 1205 Disappeared Block

    题目传送门 /* 从大到小排序,逆向思维,从最后开始考虑,无后向性 每找到一个没被淹没的,对它左右的楼层查询是否它是孤立的,若是++,若不是-- 复杂度 O(n + m),还以为 O(n^2)吓得写了 ...

  2. 密码等级:至少包含字母、大小写数字、字符中的两种 JS实现方案

    前言 密码,如果设置的太简单,很容易就被攻破,所以很多网站将密码设置的要求设置的挺严格,一般是字母.数字.字符3选2,区分大小写.对于设置得太简单的密码,予以错误提示.或者予以密码等级(低中高)显示, ...

  3. BZOJ4010: [HNOI2015]菜肴制作

    Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号 ...

  4. 利用Oracle的row_number() over函数消除重复的记录

    .select d.id,d.outer_code from dict_depts_source d order by outer_code(查看重复数据) .select d.id,d.outer_ ...

  5. 【C语言】09-字符串

    一.字符串简介 * 在Java中,一个字符串可以用String类型来存储 String s = "MJ"; C语言中没有String这种类型.其实字符串就是字符序列,由多个字符组成 ...

  6. Qt5.4 VS2010 Additional Dependancies

    Go to Linker -> General -> Additional LIbrary Directories: qtmaind.libQt5Cored.libQt5Guid.libQ ...

  7. (6) 如何用Apache POI操作Excel文件-----POI-3.10的一个和注解(comment)相关的另外一个bug

    如果POI-3.10往一个工作表(sheet)里面插入数据的话,需要注意了,其有一个不太被容易发现的bug. 被插入的工作表(sheet)里面的单元格没有包含任何的注解(comment)的时候,插入一 ...

  8. Gitolite配置管理和GIT基本操作

    简述公司版gitolite的项目配置与管理 1. 基于秘钥对的管理 1.1 客户端(需要访问代码库的机器)生成秘钥对,采用RSA加密ssh-keygen -t rsa -f path_to_store ...

  9. AMD GPU spec (public)

    http://www.x.org/docs/AMD/old/ Index of /docs/AMD/old Name Last modified Size Description Parent Dir ...

  10. Bootstrap学习笔记1

    Bootstrap在线手册 http://v3.bootcss.com,这里有详细的说明.参考.示例. Bootstrap为许多前端常用的功能都做了封装.预定义,可以直接使用. Bootstrap支持 ...