前言
我们在实现推送功能的时候,更需要了解下推送的原理机制,这样我们在发现问题时候才好定位到问题的解决办法。
推送流程和原理
Provider就是我们自己程序的后台服务器(或者是第三方的推送服务器),APNS是Apple Push Notification Service的缩写,也就是苹果的推送服务器。
上图可以分为三个阶段:
- 第一阶段:应用程序的服务器端把要发送的消息、目的iPhone的标识打包,发给APNS。
- 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。
- 第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。
APNS推送通知的详细工作流程下面这张图是说明APNS推送通知的详细工作流程:
根据图片我们可以概括一下:
- 应用程序注册APNS消息推送。
- iOS从APNS Server获取devicetoken,应用程序接收device token。
- 应用程序将device token发送给程序的PUSH服务端程序。
- 服务端程序向APNS服务发送消息。
- APNS服务将消息发送给iPhone应用程序。
有几点值得注意
首先要有一台苹果的设备,模拟器是不支持推送的,
APNS
如果需要给应用集成推送功能,就一定要用到苹果的推送服务。Apple推送通知服务(Apple Push Notification service =APNs),例如友盟,极光之类的推送服务都是向APNs推送消息,APNs再将消息推送给设备的。
推送消息传输路径:
Provider-APNs-Client App 我们的设备联网时(无论是蜂窝联网还是Wi-Fi联网)都会与苹果的APNs服务器建立一个长连接(persistent IP connection),当Provider推送一条通知的时候,这条通知并不是直接推送给了我们的设备,而是先推送到苹果的APNs服务器上面,而苹果的APNs服务器再通过与设备建立的长连接进而把通知推送到我们的设备上(参考图1-1,图1-2)。而当设备处于非联网状态的时候,APNs服务器会保留Provider所推送的最后一条通知,当设备转换为连网状态时,APNs则把其保留的最后一条通知推送给我们的设备;如果设备长时间处于非联网状态下,那么APNs服务器为其保存的最后一条通知也会丢失。Remote Notification必须要求设备连网状态下才能收到,并且太频繁的接收远程推送通知对设备的电池寿命是有一定的影响的。
deviceToken的生成
当一个App注册接收远程通知时,系统会发送请求到APNs服务器,APNs服务器收到此请求会根据请求所带的key值生成一个独一无二的value值也就是所谓的deviceToken,而后APNs服务器会把此deviceToken包装成一个NSData对象发送到对应请求的App上。然后App把此deviceToken发送给我们自己的服务器,就是所谓的Provider。Provider收到deviceToken以后进行储存等相关处理,以后Provider给我们的设备推送通知的时候,必须包含此deviceToken。
-
deviceToken到底是什么?有什么用?为什么是独一无二的?
是什么:
deviceToken其实就是根据注册远程通知的时候向APNs服务器发送的Token key,Token key中包含了设备的UDID和App的Bundle Identifier,然后苹果APNs服务器根据此Token key编码生成一个deviceToken。deviceToken可以简单理解为就是包含了设备信息和应用信息的一串编码。
有什么用:
上面提到Provider推送消息的时候必须带有此deviceToken,然后此消息就根据deviceToken(UDID + App's Bundle Identifier)找到对应的设备以及该设备上对应的应用,从而把此推送消息推送给此应用。
唯一性:
苹果APNs的编码技术和deviceToken的独特作用保证了他的唯一性。唯一性并不是说一台设备上的一个应用程序永远只有一个deviceToken,当用户升级系统的时候deviceToken是会变化的。
后台推送也是很必须的,不是所谓的多做活动,因为有些推送是条件触发的,无法做到人为推送(比如大量用户中,接单后通知发单的人)。
推送通知本身是 iOS 系统的行为,所以在 App 没有运行(没有在前台也没有在后台)的时候:仍然能够推送及接收(通知中心通知、顶部横幅、刷新 App 右上角的小圆点即 badge [以下简称角标] 等都会由系统来控制和展示)。但是收到推送时,是无法在 App 的代码中获取到通知内容的。因为沙盒机制,此时 App 的任何代码都不可能被执行。
开发中实现推送的步骤
在代码中注册推送服务;
在第一次触发这段代码的时候,会有一个系统弹窗,询问你是否允许该 App 要给你推送信息。当你选择允许时,系统会打包 App+手机唯一标识+证书 信息发送至 APNs 服务器注册推送服务,APNs 系统会对该手机安装的该 App 是否有推送权限进行验证,所以必须要加入了 Apple Deveice 的手机,使用对应 App 的推送证书才能够成功的注册。
-
如果注册成功,则可以在 AppDelegate.m 的如下方法中获取到 deviceToken,它是对 该手机+该App 组合的一个唯一标识,当使用远程推送时,只需将推送消息发给指定的 deviceToken 即可使推送信息传达给指定手机的指定 App 上。因此如果你使用第三方,就需要在这个方法里将 deviceToken 传给第三方。(在 iOS 9 为了更好的保护用户隐私,会出现多次重复删除/安装 App 导致 deviceToken 不断变化的情况。有时会出现一条推送手机会收到 2 次的问题,属于 iOS 9 系统问题)。
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [JPUSHService registerDeviceToken:deviceToken];//将 deviceToken 传给极光推送 }
综上,注册及接收推送 必须 使用真机,必须连网。
推送从 服务端 --> App 代码 的过程
- 使用你们公司或第三方的服务端向 APNs 发送推送请求(包含 推送内容+App描述+手机描述 )
- APNs 接收并验证推送请求
- APNs 利用网络搜索并定位指定设备,下发推送
- 手机收到推送,系统根据 App 状态进行处理
前台收到:
后台收到:
退出收到:
推送分几种分类
-
普通式推送
就是我们在手机上平时见到的推送
包含声音、弹窗、角标、自定义字段App 处于前台:不会弹窗,可通过 didReceiveRemoteNotification 获取推送内容([前台弹窗的方法看这里](https://github.com/pikacode/EBForeNotification)) 处于后台:会弹窗 ,无法获取推送内容 处于退出: 会弹窗,无法获取推送内容,点击图标启动,无法获取推送内容 点击推送弹窗启动,在 didFinishLaunchingWithOptions获取推送内容 推送内容类似如下: { "_j_msgid" = 200806057;//第三方附带的 id,用于在后台查询送达情况 aps = { alert = "显示内容"; badge = 1;//App 角标,可推送 n、+n、-n 来实现角标的固定、增加、减少 sound = default;//推送声音,默认系统三全音,如需使用自己的声音,需要将声音文件拖拽&拷贝至 Xcode 工程目录任意位置,并在推送时指定其文件名 }; key1 = value1;//自定义字段,可设置多组,用于处理内部逻辑 key2 = value2; }
-
后台式推送
各种显示效果跟普通推送完全一样。
必须携带alert、badge、sound中至少 1 个字段。
仅 iOS 7 以后支持。
必须在 Xcode 工程中 TARGETS - Capabilities - Background Modes - Remote notifications 开启该功能.App: 处于前台:可通过didReceiveRemoteNotification(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:(iOS 7 after) 获取通知内容。 处于后台:可通过didReceiveRemoteNotification:fetchCompletionHandler:获取通知内容 // 获取情况中与普通推送的唯一不同点,此时 iOS 系统允许开发者在 App 处于后台的情况下,执行一些代码,大概提供几分钟的时间,可以用来偷偷的刷新 UI、切换页面、下载更新包等等操作。 处于退出:无法获取通知内容。 点击图标启动,无法获取通知内容。 点击推送横幅启动,在didFinishLaunchingWithOptions获取通知内容。 通知内容类似如下: { "_j_msgid" = 2090737306; aps = { alert = "显示内容"; badge = 1; }; key1 = value1; }
-
静默式推送
没有任何展示效果。
必须携带 "content-available" = 1;,因此静默必然是后台的。
必须不携带 alert、badge、sound。
可携带自定义字段。App : 处于前台:可通过didReceiveRemoteNotification(iOS 7 before)didReceiveRemoteNotification:fetchCompletionHandler:(iOS 7 after) 获取通知内容。 处于后台:可通过 didReceiveRemoteNotification:fetchCompletionHandler: 获取通知内容 //获取情况中与普通推送的唯一不同点,此时 iOS 系统允许开发者在 App 处于后台的情况下,执行一些代码,大概提供几分钟的时间,可以用来偷偷的刷新 UI、切换页面、下载更新包等等操作。 处于退出,无法获取通知内容。 通知内容类似如下: { "_j_msgid" = 3938587719; aps = { alert = ""; "content-available" = 1; // 必带字段 }; key1 = value1; }
小结
推送的大致原理我们说了一下,其他相关知识可以查看我的其他相关知识。