JAY站

Valar Morghulis Valar Dohaeris

静下心来,用心观察 处处都透着生活的美.


极光推送的使用

苹果的APNS

ios-push-flow

  1. 用户的应用注册了APNS 消息推送功能
  2. 用户iOS设备通过SSL长连接到APNS苹果服务器,收到设备应用的注册信息后,下发给设备一个DeviceToken 给 应用
  3. 应用收到这个DeviceToken 然后推送给 自己应用的服务器 (应用到推送服务器的流程完毕)
  4. 推送服务器 发送消息到一个用户的时候, 会首先查找到 DeviceToken,然后将消息和DeviceToken 发送给 苹果的 APNS 服务器
  5. 苹果根据 DeviceToken 找到唯一的那台设备, 然后将消息 传递过去
  6. 设备收到了消息后, 会根据DeviceToken 找到应用 (推送服务器到设备应用的流程完毕)

极光推送的流程

jpush-flow

这里和上面唯一不同的就是, 应用的服务器改为了 极光的服务器

  1. 设备获取到DeviceToken 后 需要将这个 信息 上传到 极光的 服务器上面
  2. 然后 极光的服务器会 生成一个registrationID给应用
  3. 这个时候, 我们可以给应用注册一个别名, 就相当于一个 键值对, 极光服务器根据这个别名查找到registrationID, 然后又根据registrationID找到 DeviceToken, 这样 _发送消息_的时候 , 就可以将 DeviceToken 和 信息一起发送给 苹果的APNS服务器了.

实际编程的注意点

push-notification-flow

1、极光后台的生成环境和发布环境证书必须有效 push-notification-cer 2、 应用需要先注册和激活极光服务

push-notification-service

  1. 应用获取到APNS下发的DeviceToken后,需要上传到极光服务器
  2. 应用的极光登录成功方法回调,说明服务器已经收到的应用的设备信息,并且返回了registrationID
  3. 只有在registrationID返回之后,才可以设置别名

补充信息

kJPFNetworkDidLoginNotification

``` —– login result —– uid:939xxx3544 registrationID:141fe1daxxx927a72d


#### 代码中方法的调用顺序 , 一定要严格按照如下的顺序
1、添加初始化APNs代码添加初始化APNs代码

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions //Required //notice: 3.0.0及以后版本注册可以这样写,也可以继续用之前的注册方式 JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init]; entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound; if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) { // 可以添加自定义categories // NSSet<UNNotificationCategory *> *categories for iOS10 or later // NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9 } [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];


2、 APNS后 启动极光服务

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Required // init Push // notice: 2.1.5版本的SDK新增的注册方法,改成可上报IDFA,如果没有使用IDFA直接传nil // 如需继续使用pushConfig.plist文件声明appKey等配置内容,请依旧使用[JPUSHService setupWithOption:launchOptions]方式初始化。 [JPUSHService setupWithOption:launchOptions appKey:appKey channel:channel apsForProduction:isProduction advertisingIdentifier:advertisingId]; }

2、 注册APNs成功并上报DeviceToken 到极光的后台

  • (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [JPUSHService registerDeviceToken:deviceToken]; } ``` 2.5 、 实现注册APNs失败接口(可选)
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  //Optional
  NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
}

3 、 添加处理APNs通知回调方法 请在AppDelegate.m实现该回调方法并添加回调方法中的代码

#pragma mark- JPUSHRegisterDelegate

// iOS 10 Support
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {
  // Required
  NSDictionary * userInfo = notification.request.content.userInfo;
  if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
  }
  completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以选择设置
}

// iOS 10 Support
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
  // Required
  NSDictionary * userInfo = response.notification.request.content.userInfo;
  if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
  }
  completionHandler();  // 系统要求执行这个方法
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

  // Required, iOS 7 Support
  [JPUSHService handleRemoteNotification:userInfo];
  completionHandler(UIBackgroundFetchResultNewData);
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

  // Required,For systems with less than or equal to iOS6
  [JPUSHService handleRemoteNotification:userInfo];
}

4、如果项目中需要接收自定义信息的功能, 则需要添加监听自定义消息的方法 (可选)

只有在前端运行的时候才能收到自定义消息的推送。
从jpush服务器获取用户推送的自定义消息内容和标题以及附加字段等

获取iOS的推送内容需要在delegate类中注册通知并实现回调方法。
在方法didFinishLaunchingWithOptions 加入下面的代码:


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions {
     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
    [defaultCenter addObserver:self selector:@selector(networkDidReceiveMessage:) name:kJPFNetworkDidReceiveMessageNotification object:nil];
}
并且实现通知的回调方法 networkDidReceiveMessage
- (void)networkDidReceiveMessage:(NSNotification *)notification {
        NSDictionary * userInfo = [notification userInfo];
        NSString *content = [userInfo valueForKey:@"content"];
        NSDictionary *extras = [userInfo valueForKey:@"extras"]; 
        NSString *customizeField1 = [extras valueForKey:@"customizeField1"]; //服务端传递的Extras附加字段,key是自己定义的
}


5、 监听极光登录成功的通知,查看是否入库极光成功, 在这个方法中可以 设置别名; 不过应用的逻辑 是 用户登录成功后才可以推送,所以在这里增加了判断用户是否登录的方法。

 (void)networkDidLogin:(NSNotification *)notification {
    DDLog(@"jpush已登录,通知详情为%@",notification.userInfo);
    //向极光注册别名
    User *user = [UserManager sharedInstace].usr;
    if( user.isLogin && user.device.tmpPushAliasName ){
        user.device.pushAliasName = user.device.tmpPushAliasName;
    }
}

6、真机调试该项目,如果控制台输出以下日志则代表您已经集成成功。

2017-05-17 11:24:05.968565+0800 DDRide[22015:4672102]  | JIGUANG | I - [JIGUANGRegistration] 
----- register result -----
uid: 942xxxx1223
registrationID:13165fxxxx97541104 
2017-05-17 11:24:05.970270+0800 DDRide[22015:4671830] [函数名:-[JpushManager networkDidRegister:]]----[行号:74] 
jpush已注册
2017-05-17 11:24:06.108945+0800 DDRide[22015:4672119]  | JIGUANG | I - [JIGUANGLogin] 
----- login result -----
uid:942xx61223 
registrationID:13165ffxxxx7541104
2017-05-17 11:24:06.112224+0800 DDRide[22015:4671830] [函数名:-[JpushManager networkDidLogin:]]----[行号:78] 
jpush已登录,通知详情-(null)
2017-05-17 11:24:06.266877+0800 DDRide[22015:4672102]  | JIGUANG | I - [JIGUANGDeviceTokenReport] try to upload device token:152ff6a08ec7927xxxxxxxxxxxe603cd642f5f4cab8cae10ab8e
2017-05-17 11:24:06.437331+0800 DDRide[22015:4672102]  | JIGUANG | I - [JIGUANGBadgeNumberReport] set badge:0 succeed
2017-05-17 11:24:06.535148+0800 DDRide[22015:4672119]  | JIGUANG | I - [JIGUANGDeviceTokenReport] upload device token success

7、 新增功能 接收本地通知 , 该代理方法已经适配iOS10

- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^) (NSInteger))completionHandler; 
   // if (![notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { 
   // 本地通知为notification 
   // }

- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler: (void (^)())completionHandler; 
  // if (![response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { 
  // 本地通知为response.notification 
  // }

测试结果

  1. 模拟器不能测试极光推送,因为只有真机具备UDID, 才能够生成DeviceToken
  2. 生产环境证书失效,测试失败
  3. 登录成功后,查看 registrationID141fe1dxxxx7a72d ,入库极光成功,马上测试别名推送(前台),成功
  4. 后面等待一段时间后,进入后台测试根据registrationID 推送成功, 推送别名149498xxxx0546 成功.
  5. 更换设备后, 没有登录的情况下, registrationID变化为13165ffxxxx7541104
  6. 测试 没有登录的时候 后台registrationID推送成功 , 别名14949xxxx12183516
  7. 这里有个需要注意的地方是,如果服务器是根据别名推送,且每次登录会产生新的别名,那么情况就比较麻烦.

对于单点登录的问题, 环境: 设备A已经登录, 设备B同账户登录, 更新了别名, 服务器根据同用户的旧别名发送一个通知, 设备A接收到通知后,退出登录. 可以这样处理.服务器保留上一次的别名和最新的别名. 如果自己的服务器保存的是最新的别名, 根据最新的别名推送,则最新登录的设备是能够收到消息,而使用旧的别名,新设备不会接受到, 只有上一个登录的设备会接收. 因为一个别名对于一个注册ID, 一个注册ID则对于一个 DeviceToken,而一个DeviceToken则能够寻找到唯一设备的唯一应用, 这种一一对应的关系,只要某一环出了问题,推送就不可以送达,更不要说设备如果网络环境差的话,同样APNS不会推送到设备应用上.

常见问题

1、 iOS 9系统,应用卸载重装,APNs返回的devicetoken会发生变化,开发者需要获取设备最新的Registration id。请在kJPFNetworkDidLoginNotification的实现方法里面调用”RegistrationID”这个接口来获取 RegistrationID。 2、 为什么iOS收不到推送消息?

  • iOS接受消息必要条件是:应用程序的证书要和你上传到jpush portal上的证书对应,如果你的程序是直接在xcode上运行的,你的应用部署环境必须是开发状态才能收到APNS消息。
  • 确认 appKey 在 SDK 客户端与 Portal 上设置是一致,其他环节也按照文档正确地操作。
  • 检查 Portal 上上传的证书,是 APNs (Push) 证书。
  • 再次检查证书选择是否正确,参考iOS 证书设置指南89。
  • 推送时选择的环境与测试设备的打包环境必须一致。测试说明109
  • 严肃说明:api推送的时候通过参数 apns_production 来指定推送环境,false为开发环境,true为生产环境。V3 api不带此参数则默认为生产环境,V3 api封装的 sdk 默认为开发环境。如果api有传apns_production则以此值为准。
  • 另外,请确认设备网络是否正常,如果设备离线,期间推送多条,apns服务器只会离线保留一条

3、 应用产生的设备信息是否变化 自iOS9开始,卸载重装、长时间关闭推送后又打开等情况 会产生新的token,因此有对应的新的registrationID。(如果用了idfa,在用户没有选择限制广告跟踪,并且未点击『还原广告标识』时,这个值是不会变的,对应的token也不会改变。)

 温馨提示:
 * Registration id 需要在执行到kJPFNetworkDidLoginNotification的方法里获取

 * extern NSString * const kJPFNetworkDidReceiveMessageNotification; // 收到自定义消息(非APNS)
 其中,kJPFNetworkDidReceiveMessageNotification传递的数据可以通过NSNotification中的userInfo方法获取,包括标题、内容、extras信息等
 * 主要是在项目的登录成功或者自动登录后,使用用户的唯一标示进行绑定,或者根据需求添加一些前缀
 
 * 用户进行退出登录的方法里添加去除绑定即可,值得注意的是用到即时通讯的话,被挤下线也要去除绑定,已被坑,贴代码:
 //没有值就代表去除
 [JPUSHService setTags:[NSSet set]callbackSelector:nil object:self];
 [JPUSHService setAlias:@"" callbackSelector:nil object:self];
 [JPUSHService setTags:[NSSet set] alias:@"" callbackSelector:nil target:self];

参考链接

最近的文章

iOS 高效编码(一)

在类的头文件中尽量少运入其他头文件1、 除非有必要,否则不要引入头文件. 一般来说,应该在类的头文件中使用向前引用来声明一个类,并在实现文件中,引入那些类的头文件.好处: 减少编译时间,解决互相引用问题,降低类之间的耦合.2、 如果无法使用向前引用,比如声明某个类遵守一项协议.这种情况下,尽量把声明移到”class-continuation分类”中. 如果不行就把协议单独放在一个头文件中,然后将其引入.两者的区别在于: 协议与遵循协议类的关系,是否有强烈的依赖关系.比如委托协议(deleg...…

iOS编码继续阅读
更早的文章

iOS自动化打包分发

为什么要自动化 节省时间,快速迭代: 减少重复繁琐的过程,本地继续编码,使用工具自动拉取远程库代码后打包 纠错: 打包出错,会自动查找到编译错误 快速分发多个版本: 配置好不同分支的打包策略,可以将打包任务移交测试,分工更明确工具 Jenkins CI Flow CI Travis CI Hudson CI Circle CI本文只关注 Jenkins 的使用Jenkins 的三种安装方式 war pkg homebrew推荐使用 homebrewjenkins 安装...…

iOS自动化继续阅读