相信大家对推送这项技术并不陌生。如果没听说过,那么作为一个充满好奇心的孩子,你一定想过这个问题:睡觉前我明明关闭了淘宝、网易新闻等 app,为什么第二天他们又自动出现在我手机的通知栏上呢?这其实就是推送系统干的好事:在你睡觉的时候,服务器悄悄的向你的手机推送了一个消息,然后唤醒了你已经关闭的 app。事实上,无论你愿意与否,现在大多数 ‘有节操’ 的 app,都已经内置了推送系统,并时刻准备着登上你的通知栏的 ‘头条’。

iOS开发APP收到推送消息没有弹窗 app打开才有推送_iOS开发APP收到推送消息没有弹窗

传统的 app 架构里,通常是 app 主动向服务器请求数据,服务器被动的提供数据。以新闻客户端 app 为例:app 被用户打开的时候,会通过网络 (无论 3g、4g 还是 wifi) 连接到服务器上,向服务器请求最新的新闻。服务器收到请求,从自己的数据库里查询最新的新闻,返回给 app。app 收到服务器返回的数据,经过一系列的解析处理操作,最终把最新的新闻呈现给用户。一次通信就完成了。然而如果此时服务器上又有了新的新闻,无论多么重要,在用户没有主动刷新的情况下,是没有办法让用户看到的。推送就是为了解决这样的困境的,它给了服务器一个展示自我的机会,主动连接上所有的 app,告诉他们我有新的新闻了,你们再来请求一次吧,于是收到推送的 app(即时此时已经被用户关闭了)又去服务器请求最新的新闻,这样用户就能看到最新的新闻了。

iOS开发APP收到推送消息没有弹窗 app打开才有推送_服务器_02

从技术上来讲,实现一个推送系统需要服务器端和终端的配合。一种方法是轮询,也就是不停的向服务器发起请求。这其实很好理解,作为 app,我既然不知道什么时候会发生新的新闻,那我一遍一遍的问好了,而且我知道这样一定会成功的。显而易见,这种方法 app 端费时费力不说,电量流量也扛不住啊,服务器要处理如此量大的请求,必然也是非常头疼的。另一种方法是服务器和 app 建立一个长时间连接的通道,通过这个通道,不仅 app 可以向服务器请求数据,服务器也可以向 app 发送数据,看起来非常完美,但是如果 app 被用户关闭的话,通道就断掉了。好在 android 系统给 app 提供了一个这样的环境,app 可以启动一个后台服务来维持这个通道,即使 app 被关掉了,服务依然可以运行,通道依然还在工作(ios 后面会讲)。回到前面的例子,你在睡觉前关掉了淘宝,但是并没有关闭淘宝的后台服务,淘宝依然可以接收服务器推送来的指令,把自己的唤醒。

那么如何维持这样的一条长时间连接的通道呢?就好比两个人打电话,一开始聊的热情有来有往,后来慢慢沉默下来了,几分钟之后,电话的另一头没有任何动静,如何知道那边的人还在呢?很简单,只需要另一头的人每隔几分钟说一个字就行。同样的道理,app 会每隔一段时间向服务器报告自己还活着,就像心跳一样,服务器收到后,就知道这个通道是可以继续使用的了。然而天下没有免费的午餐,发送心跳是有代价的,一般手机锁屏之后,为了省电 CPU 是出于休眠状态的,然而发送心跳就会唤醒 CPU,必然会增加电量的消耗。这还只是一个长连接通道的情况,如果手机里装了 2、30 个带有推送的 app 呢?先别急着抱怨,聪明的 android 工程师和 ios 工程师早就想到了这一点,他们分别设计了 GCM 和 apns 来解决多个 app 有多个长连接通道的问题。以 apns 为例,ios 开通了一条系统级别的长连接通道,通道的一端是手机的所有 app,另一端是苹果的服务器。app 的服务器如果有新的消息需要推送的话,先把消息发送到苹果的服务器上,再利用苹果的服务器通过长连接通道发送到用户手机,然后通知具体的 app。这样就做到了即使手机安装了 100 个 app,也只需要向一条通道里发送心跳。

iOS开发APP收到推送消息没有弹窗 app打开才有推送_长连接_03

回到 Android,系统提供的 GCM 只能在 Android2.2 以上才能使用,3.0 以下必须要安装 Googleplay 并登陆了 Google 账号才能支持。而国内发行的手机大多是阉割掉了 google 服务的。因此,对于 Android 系统来说,各家 app 只能各显神通,开发自己的专用长连接通道了。然而这时候他们遇到了 app 的天敌:管家和卫士们。前文说了,app 想要及时收到服务器推送的消息,关键在于自己与服务器的长连接通道不被关闭,也就是自己的后台服务可以一直在后台运行,而管家和卫士们的一键清理功能就是专治这种 “毒瘤” 的。道高一尺魔高一丈,app 在与管家和斗士们的长期斗争中,总结了一系列躲避被清理掉的方法,什么定时自启能力、什么相互唤醒、什么前台进程等等,当然这就是另一个话题了,我们后面会讲到。

总结起来,app 和后台的连接方式有两种。一种叫 pull,也叫轮询,就是定期的不断向后台请求,缺点是耗电,费流量,不环保。对于一名有追求的程序员,他应该会比较恶心这种方式的,你千万不要对他说,我不管你怎么实现,我就要这种效果这种傻逼话了,凡事应该找到最优路径。另一种叫 push,app 和后台一直维持了一条通信通道,两端不定期的就会偷摸的约会,告诉对方 “I ‘m Here”,也能顺带把信息互相携带了。缺点是要维持一条长连接通道,这条通道容易被其他程序杀死,要多想复活办法。

作者:给产品经理讲技术