zoukankan      html  css  js  c++  java
  • iOS Push详述,了解一下?

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

    本文由WeTest质量开放平台团队发表于云+社区专栏

    作者:陈裕发, 腾讯系统测试工程师

    商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。

    原文链接:http://wetest.qq.com/lab/view/380.html

    WeTest 导读


    本文主要对iOS Push的在线push、本地push及离线(远程)push进行梳理,介绍了相关逻辑,测试时要注意的要点以及相关工具。小小的Push背后蕴藏着大大的逻辑!


    Push种类

    一、在线push


    在线push:当用户在线(APP在前台)时,收到的状态栏的消息提醒,称为在线push。这个功能与苹果系统无关,是我们自己的APP开发的一种功能,该push与设置中是否打开“通知”无关。

    这里以iOS Qzone为例,当APP在前台时,自己发的说说被点赞了,收到的在线push如下:

    img1.png

                  Qzone在线push 
    

    二、离线(远程)push


    离线push:当APP在离线(kill掉进程、切到后台、锁屏)时,收到的消息提醒,称为离线push。离线push是需要经过苹果的APNs服务器才可以推送到某台设备的某个APP上的,这是和本地push的本质区别。push与设置中是否打开“通知”有关。

    这里最简单的以大家常用的手机QQ为例,当APP在后台、锁屏或者被kiil了进程时,收到了消息:

    img2.png

       离线push 
    

    1、静默push

    静默push用的场景不较少,这里只做简要介绍。

    首先我们看看离线(远程)push与静默push的区别:

    普通离线(远程)push:收到推送后(有文字有声音),点开通知,进入APP后,才执行-- (void)application:(UIApplication didReceiveRemoteNotification:(NSDictionary fetchCompletionHandler:(void result))handler )application )userInfo (^)(UIBackgroundFetchResult

    静默push:收到推送(没有文字没有声音),不用点开通知,不用打开APP,就能执行(void)application:(UIApplication )application)userInfo didReceiveRemoteNotification:(NSDictionary fetchCompletionHandler:(void (^)(UIBackgroundFetchResultresult))handler,用户完全感觉不到。

    所以静默push又被我们称做 Background Remote Notification(后台远程推送)。静默推送是在iOS7之后推出的一种推送方式。它与其他推送的区别在于允许应用收到通知后在后台(background)状态下运行一段代码,可用于从服务器获取内容更新。

    三、本地push


    本地push:本地推送和远程推送的功能是一样的,都是要提醒用户去做某些事情。但是和远程推送不同的就是本地推送是不需要设备联网的,而远程推送是必需要设备联网的,因为只有联网状态下,才能和苹果的APNs服务器建立长连接,从而推送消息。本地推送是由App自己设定的,并且发送给安装此App的这台设备,属于一对一的对应关系。比较典型的应用是闹钟类似的场景。该push与设置中是否打开“通知”有关。

    最容易看到本地push的场景,可以直接在手机设置一个计时器,计时器时间到了就会弹出本地push:

    img3.png

             本地push
    

    img4.png

    由于本地push原理和作用相对于在线push和离线push都更为简单明了,下文主要介绍在线push和离线push。

    本地push实现

    一、 iOS10以前本地push弹出方式


    试验过iOS10以前的本地push方法在iOS10+的系统也能使用,不过可能有些参数不生效。

    1、立即展示( iOS10以前)

    本地push稍微简单,有两种方式可以调用,一种是presentLocalNotificationNow方法,立即展示本地push:

    img5.png

    2、延迟展示( iOS10以前)

    另一种是用scheduleLocalNotification方法按计划来弹本地推送:

    img6.png

    如果使用这种方法,需要对推送的时间进行设置,举个例子,设为5秒后:

    img7.png

    二、设置本地push内容( iOS10以前)


    img8.png

    其中alertBody是消息内容锁屏与不锁屏时效果如下:

    img9.png

         本地push效果
    

    applicationIconBadgeNumber是消息数量,我们可以看到这里设置为66:

    img10.png

          消息数
    

    三、处理本地push ( iOS10以前)


    1、 App没有启动情况下处理本地push

    这种情况下,当点击通知时,会启动App,而在App中,开发人员可以通过实现AppDelegate中的方法:- (BOOL)application:(UIApplication)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions,然后从lauchOptions中获取App启动的原因,若是因为本地通知,则可以App启动时对App做对应的操作,比方说跳转到某个画面等等。

    img11.png

    2、App运行在后台及前台

    上面的2种情况的处理基本一致, 不同点只有当运行再后台的时候,会有弹窗提示用户另外一个App有通知,对于本地通知单的处理都是通过AppDelegate的方法:- (void)application:(UIApplication )application didReceiveLocalNotification:(UILocalNotification *)notification来处理的。

    img12.png

    四、iOS10以后本地push弹出方式


    iOS10以后,本地通知可以由使用 UNUserNotificationCenter来管理。

    创建方法:

    img13.png

    接下来需要需创建一个包含待通知内容的 UNMutableNotificationContent 对象:

    img14.png

    在iOS上可以通过以下几种触发器来触发本地push:

    ● UNCalendarNotificationTrigger 传送本地通知的日期和时间。

    ● UNTimeIntervalNotificationTrigger 传递本地通知之前必须过期的时间。

    ● UNLocationNotificationTrigger 用户必须达到的地理位置才能提供本地通知。

    ● UNPushNotificationTrigger 表示通知是从Apple推送通知服务发送的对象。

    假如以时间间隔(TimeInterval)来触发,则设置触发器代码为:

    img15.png

    推送本地push的代码为:

    img16.png

    在线、离线(远程)push流程

    一、在线push流程


    在线push相对简单,因为是内部实现,具体流程如上面所示。

    1、判断app是否在线

    此处可以根据APP自身的后台策略如上一次与后台交互的时间等方法来判断APP是否在线或者离线。认为在线,会发送在线push,否则,发送离线push。

    2、在线push特点

    ● 在线push有以下几个特点:

    ● 不需要经过苹果APNs。

    ● 需要自己实现长链接。

    ● 代码在app内部实现。

    二、离线(远程)push流程


    img17.png

    离线push流程
    

    主要流程为:

    ● 服务器端将消息先发送到苹果的APNs

    ● 由苹果的APNs将消息推送到客户的设备端

    ● 由iOS系统将接收到的消息传递给相应的App。

    简而言之离线push是苹果系统的行为,与app状态无关,能够直接推送到指定手机的指定app。

    在进一步了解离线push前,我们有必要先了解几个名词。

    1、离线push名词解释

    —APNs

    APNs:Apple Push Notification service(苹果推送通知服务)。

    APNs主要用于以下场景:当用户主动杀掉 APP,或者 APP 进入后台超过约定时长时,APP会被kill,这样保障了前台 APP 的流畅性,也延长了手机的使用时长,获得了较好的用户体验,但是这也意味着,服务器无法主动和用户交互(如推送实时消息等),所以苹果推出了 APNs,允许设备和服务器分别与苹果的推送通知服务器保持长连接状态。

    关于APNs的更新有以下几点

    ● iOS 8以后,APNs推送的字节是2k,iOS8以前是256字节

    ● iOS 9以后APNs支持HTTP/2协议栈,优化长连接,具有标准的HTTP返回和管道复用技术

    ● iOS 10以后,推送的字节是4k,APNs可根据推送消息的唯一标示符查询某条消息是否被用户阅读,可更新某一推送消息,而不用发重读的多条消息

    关于APNs更全面的介绍可以看官方文档:

    https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1

    —payload

    什么是payload?对于每一条发送给APNs的推送消息,都包含一个payload,通常是组成了一个JSON的Dictionary,这其中必不可少的是aps属性,它对应的value也是一个Dictionary,包含一些但不限于以下内容:标题、副标题、内容、附件、category等,如

    img18.png

    —device token

    什么是device token?我们看一下官方的简介:

    device token: APNs uses device tokens to identify each unique app and device combination. It also uses them to authenticate the routing of remote notifications sent to a device.(device token是APNs用于区分识别每个iOS设备和设备上不同app的一个标识符,还可以用于APNs通过它将推送消息路由到指定设备上)

    即:device token里包含了device id和bundle id的信息,但是device id和bundle id不会确定唯一的device token。

    但是,这里有个坑,查资料得知,iOS8及之前的iOS系统,对于同一部手机,如果卸载后重装APP的话,device token是不会变的,在token变了以后,老的token,就被认为是无效了,苹果不会对这部分无效的token推送。但是,对iOS9及以后的iOS系统,对于同一部手机,卸载后重装APP的device token是会发生变化的,而且老的token不会无效,还可以正常推送,这应该是苹果的一个bug,但是苹果也没有修复这个问题,所以这个需要开发者自己来解决,否则容易出现一个app收到多个push的问题。官方的说法是:

    To protect user privacy, do not use device tokens to identify user devices. Device tokens change when the user updates the operating system and when a device’s data and settings are erased. As a result, apps should always request the current device token at launch time.(即此举为了保护用户隐私,device token会在更新系统、擦除设置重置后变化,在一定时间后会过期)

    2、离线push详细流程

    知道了以上概念后我们重新来看一下离线(远程)push的详细流程:

    img19.png

        离线push详细流程
    
    1. 首先是应用程序注册消息推送。

    2. iOS跟APNS Server要deviceToken。应用程序接受deviceToken。

    3. 应用程序将deviceToken发送给PUSH服务端程序。

    4. 服务端程序向APNS服务发送消息。

    5. APNS服务将消息发送给iPhone应用程序。

    值得注意的是,当由于用户反复卸载重装程序(虽然概率很小)等原因导致多个device Token指向同一台设备的同一个app,又把多个device Token发给APNs时,用户就会收到多条push。苹果APNs是不会对多个device Token是否指向同一台设备的同一个app做校验的,所以需要后台来做去重等处理保证用户不会收到多条push。

    三、对离线(远程)push的响应


    1、iOS 7以上对离线(远程)push时的响应

    iOS 7以上关于接受离线push有两个函数

    img20.png

    那么这两个函数有什么区别呢?其实这两个方法都是用来处理离线push的。

    差别就是,如果app在前台是收到离线(远程)push,那么就会调用

    img21.png

    相对的,如果在后台或者杀进程情况下,点击收到的离线push,那么就会调用,如果没有实现

    img22.png

    则会调用

    img23.png

    若实现了前者,就只调用前者。

    2、iOS 10以上对离线(远程)push的响应

    iOS10对push的处理主要增加了两个方法

    img24.png

    其中前者是对APP在前台时收到push时的处理,后者是点击push进入APP执行的函数。

    用得比较多的是后者,我们可以举个例子,点击push进入APP后如何获取push的消息、角标、标题等内容:

    img25.png

    iOS 10关于push的一些新特性

    iOS10新增的UserNotifications框架,主要有了这样几方面的更新:

    ● 用UserNotifications框架替换了原先与通知相关的接口,通知文字可分为title、subtitle和body三部分,通知可携带附件

    ● 系统在展示通知之前,可以唤起app附带的service extension,并且允许它改动通知的内容

    ● 用户在对通知右滑查看、下拉或者3d touch的时候,通知会展开,展开后页面的布局可以由app附带的content extension来决定

    一、push的多样性


    iOS10以前的push只有文字,甚至没有标题。

    iOS10以后的push更加多样化,可以有主标题,副标题,甚至还有附件,这里以我司的腾讯新闻为例(有标题,内容,和附件):

    img26.png

     腾讯新闻push
    

    3D touch点入详情以后:

    img27.png

       腾讯新闻push详情
    

    这里我们惊奇的发现,除了可以携带图片这样的附件、push还能展开详情以外,进入详情以后,下面还多了“打开”、“收藏”、“不感兴趣”这些选项,这里就涉及到以下iOS10的新特性。

    二、push携带附件


    因为payload有大小限制,所以如果remote notification想要携带附件,那么payload上只能带上如附件下载地址之类的信息,等通知到达客户端后由service extension下载附件到本地,然后在初始化UNNotificationAttachment对象时传入附件在本地的URL。

    img28.png

    初始化UNNotificationAttachment对象时,可以传入option参数。这里的option参数可以强制指定附件的类型,可以选择是否展示缩略图,以及缩略图截取自附件的哪一帧、哪一部分。

    目前iOS10通知只将几种格式的图片、音频和视频作为附件,附件的大小也有一定限制,具体可以看官方文档中的限制说明。

    关于附件的更加详细的说明,可以参考官方文档:

    https://developer.apple.com/documentation/usernotifications/unnotificationattachment?language=objc

    三、携带action的通知


    上面提到的“打开”、“收藏”、“不感兴趣”这些选项其实就是push携带的action,其实从iOS8开始,通知已经可以携带action了。而在iOS10中,通知的action被放在了更明显的位置,与action相关的接口也有了很大变化。

    决定一个通知应该有哪些action呢?在payload中,这是由category字段决定的。如果我们希望一个通知能携带若干个action,我们就需要将若干个action和一个category绑定起来。通知到达前端后,系统会根据category的名字来决定要给这个通知展示哪些action:

    img29.png

    怎么得知用户选了哪个action并做出相应操作呢?这需要给UNUserNotificationCenter指定一个delegate:

    img30.png

    然后在delegate的类中实现

    img31.png

    方法:通过response.notification.request.content.categoryIdentifier和response.actionIdentifier就可以得知用户选择的action了。

    四、改变push内容


    这里主要讲应用的比较多的离线(远程)push的改变push方法

    1、改变本地push内容

    本地push,只要request的id一样,那么就可以更新推送:

    更新的例子:

    img31.png

    img32.png

    此外,还有删除所有推送等,都在UNUserNotificationCenter.h中实现。

    2、改变离线(远程)push内容

    目前远程push只支持更新push内容,更新需要通过新的字段apps-collapse-id来作为唯一标示。方法是在HTTP/2 请求头中使用相同的apns-collapse-id,这样收到同样的apns-collapse-id的push时,push内容便会更新。

    使用场景:比较容易理解的一个场景就是球赛比分,比如现在是1:0,如果变成1:1的话,只需要刷新原来的新闻,这样用户就不会因为同一场比赛收到多条push。

    五、两个extension


    有两个与push相关的extension,可能我们会好奇这两个extension有什么不同,为什么需要两个?它们分别实现什么功能呢?

    img33.png

        push相关extension
    

    1、notification service extension

    给app添加notification service extension后,系统会在收到通知后唤醒它,并允许它修改通知的内容,之后再展示这个通知。

    service extension只对remote notification起作用,local notification是无法唤起它的。

    如果想要让系统唤起service extension的话,payload必须符合这样几个条件:

    1. 必须增加mutable-content字段并为1,这表示允许客户端修改这个通知:

    payload(举例)如下:

    img34.png

    2)这个通知必须展示一个alert,如果只是一个修改badge的通知的话,是不会唤起service extension的

    3)静默推送是不能唤起service extension的,所以payload中不能有”content-available” : 1字段

    所以,通过这个notification service extension,你可以在接收到推送之后、展示推送之前处理一些事情,比如说更新一下推送内容,或者在后台做一些其他事情。

    2、notification content extension

    另一项notification content extension用于完全自定义推送展开后的视图。上面腾讯新闻的展开后的视图就是通过这个notification content extension实现的。

    依然以腾讯新闻为例子:

    img35.png

          展开界面
    

    这里Notification Content Extension大展拳脚的地方,在这里可以自定义绘制不同的内容,将希望展现给用户的额外信息可以加载这里。

    下半部分的notification action的实现就是在上面提到的“携带action的通知”。

    测试要点

    img36.png

    Q&A

    Q:离线push,支持角标(badge)在本地角标数值上+1这样的操作吗?

    A:不支持。如果是自己实现push服务的话,需要自己的后台将角标值badge发送个APNs服务器,有些APP使用第三方push SDK除外。

    Q:如果重复收到离线push,可能是什么情况

    A:

    1)iOS9之后卸载重装后生成新的deviceToken,后台对多个deviceToken都发送了push

    2)后台对注销了的账号也发送了push。

    总而言之一般是后台的逻辑出现了问题,而不是APNs服务器出现问题。

    Q:直接卸载APP,还能收到离线push吗?

    A:不会收到。直接卸载APP,虽然后台不知道APP被卸载了,仍然会对之前的账号发送push,但是由于手机上没有对应APP,所以并不会收到push。

    Q:为什么有时候全新安装APP就立马有红点角标?

    A:这是因为卸载该APP时有红点角标。每个 APP 的角标都是存在 iOS 手机系统里的,开发无法修改,所以此时卸载前有角标,重新安装也会有角标。但是,APP 卸载之后超过一天的时间再重装,那么角标就会被系统清空,届时也不会有新安装的 APP 就有角标的情况存在。

    相关工具

    Knuff离线push工具下载链接:https://github.com/KnuffApp/Knuff/releases

    使用方法也比较简单

    img37.png

    比如我的payload输入如下:

    img38.png

    得到的应该是有“Knuff测试”文字,和角标数变为999,我们可以看下结果,与预料是一致的:

    img39.png

         预期结果
    

    有了这个工具也更加方便了我们的iOS push的调试。

    参考资料iOS推送之远程推送(iOS Notification Of Remote Notification):https://www.jianshu.com/p/4b947569a548玩转 iOS 10 推送 —— UserNotifications Framework(合集):https://www.jianshu.com/p/f57e2045f711用iOS10 UserNotifications框架来接收remote notification:https://www.jianshu.com/p/b6be6310f866iOS10推送通知进阶(Notification Extension):https://www.jianshu.com/p/78ef7bc04655

    相关阅读
    系统负载能力浅析
    搭建公众号自动回复功能
    【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

    此文已由作者授权腾讯云+社区发布,更多原文请点击

    搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

    海量技术实践经验,尽在云加社区

  • 相关阅读:
    自定义View的ToolBar布局报错Error:(2) No resource identifier found for attribute 'context' in package 'c
    在学git之主分支 branch
    获取发布版SHA1
    关于开启线程与UI的操作
    播放音频和视频(VideoView控件)
    通知栏Notification的应用
    Android 真机调式 Installation failed with message 远程主机强迫关闭了一个现有的连接。. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing. WA
    运行程序申请危险权限
    mysql乐观锁总结和实践
    Nginx配置文件nginx.conf中文详解
  • 原文地址:https://www.cnblogs.com/qcloud1001/p/9855041.html
Copyright © 2011-2022 走看看