zoukankan      html  css  js  c++  java
  • iOS,推送通知

    推送通知

    推送通知跟NSNotification有所区别: 
    1> NSNotification是抽象的,不可见的 
    2> 推送通知是可见的(能用肉眼看到)

    iOS中提供了2种推送通知: 本地推送通知, 远程推送通知 
    1> 本地推送通知(Local Notification) 
    2> 远程推送通知(Remote Notification)

    推送通知可以不让在前台运行的app,告知app内部发生了什么变化,比如:有新的内容,新消息等.

    推送通知的使用: 
    发出推送通知时,如果当前程序正运行在前台,那么推送通知就不会被呈现出来 
    点击推送通知后,默认会自动打开发出推送通知的app 
    不管app打开还是关闭,推送通知都能如期发出

    推送通知有5种呈现效果: 
    1. 在屏幕顶部显示一块横幅(显示具体内容) 
    2. 在屏幕中间弹出一个UIAlertView(显示具体内容,使用较少) 
    3. 在锁屏界面显示一块横幅(锁屏状态下,显示具体内容) 
    4. 播放音效(提醒作用) 
    5. 更新app图标的数字(说明新内容的数量)

    推送通知分为本地推送通知和远程推送通知,下面一一介绍.

    本地推送通知

    本地推送通知不需要服务器的支持,不需要联网就可以发送通知.通常本地推送通知用于定时提醒用户,比如清理垃圾,淘宝购物,纪念日提醒等任务. 
    在苹果官方给出了本地推送通知的一些属性,以及使用.

    属性介绍:

      @property(nonatomic,copy) NSDate *fireDate; // 设置本地推送的时间
      @property(nonatomic,copy) NSTimeZone *timeZone; // 时区(一般设置为[NSTimeZone defaultTimeZone] ,跟随手机的时区)
      @property(nonatomic) NSCalendarUnit repeatInterval; // 没隔多久重复发出一次
      @property(nonatomic,copy) NSCalendar *repeatCalendar; // 设置日期
      @property(nonatomic,copy) CLRegion *region NS_AVAILABLE_IOS(8_0); // 比如某一个区域的时候发出通知
      @property(nonatomic,assign) BOOL regionTriggersOnce NS_AVAILABLE_IOS(8_0); // 进入区域是否重复
      @property(nonatomic,copy) NSDictionary *userInfo;  // 附加的额外信息
      @property(nonatomic,copy) NSString *alertBody; // 消息的内容
      @property(nonatomic) BOOL hasAction; // 是否显示alertAction的文字(默认是YES)
      @property(nonatomic,copy) NSString *alertAction;  // 设置锁屏状态下,显示的一个文字
      @property(nonatomic,copy) NSString *alertLaunchImage; // 启动图片
      @property(nonatomic,copy) NSString *soundName;   //  UILocalNotificationDefaultSoundName
      @property(nonatomic) NSInteger applicationIconBadgeNumber; // 应用图标右上角的提醒数字
      @property(nonatomic,copy) NSArray *scheduledLocalNotifications; // 获得被调度(定制)的所有本地推送通知(已经发出且过期的推送通知就算调度结束,会自动从这个数组中移除)
    

    使用方法:

    创建本地通知

    UILocalNotification *localNoti = [[UILocalNotification alloc] init];

    调度本地推送通知(调度完毕后,推送通知会在特地时间fireDate发出)

    [[UIApplication sharedApplication] scheduleLocalNotification:localNoti];

    取消调度本地推送通知
    - (void)cancelLocalNotification:(UILocalNotification *)notification;
    - (void)cancelAllLocalNotifications;
    • 立即发出本地推送通知
    - (void)presentLocalNotificationNow:(UILocalNotification *)notification;

    注意: 
    iOS8.0以后在本地推送通知上新加了一些新功能,为了用户体验,以及更加人性化,如果要使用本地通知,需要得到用户的许可. 
    需要在AppDelegate中添加如下代码

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        /*
         UIUserNotificationTypeNone    = 0,      没有,没有本地通知
         UIUserNotificationTypeBadge   = 1 << 0, 接受图标右上角提醒数字
         UIUserNotificationTypeSound   = 1 << 1, 接受通知时候,可以发出音效
         UIUserNotificationTypeAlert   = 1 << 2, 接受提醒(横幅/弹窗)
         */
        // iOS8需要添加请求用户的授权
        if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
            UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
            [application registerUserNotificationSettings:settings];
        }
    }

    点击本地推送通知 
    当用户点击本地推送通知,会自动打开app,这里有2种情况

    1> app并没有关闭,一直隐藏在后台(运行在后台) 
    让app进入前台,并会调用AppDelegate的下面方法(并非重新启动app)

    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
    • 1
    • 1

    2> app已经被关闭(进程已死) 
    启动app,启动完毕会调用AppDelegate的下面方法

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

    launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象

    若要实现界面的跳转,需要分清当前应用程序的所处状态,进行判断: 
    1> 若是当前应用在后台运行,接收到通知时,要想进行界面的跳转,可以在didReceiveLocalNotification:方法中实现跳转界面的方法 
    2> 若是当前的应用程序已经关闭,我们在前面说到,当应用关闭,推送通知也会如期发送.但此时,是不会走didReceiveLocalNotification:方法的,那我们只有didFinishLaunchingWithOptions:方法利用,launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象,实现跳转的功能.

    下面是演示代码:

    #import "AppDelegate.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        /*
         UIUserNotificationTypeNone    = 0,      没有,没有本地通知
         UIUserNotificationTypeBadge   = 1 << 0, 接受图标右上角提醒数字
         UIUserNotificationTypeSound   = 1 << 1, 接受通知时候,可以发出音效
         UIUserNotificationTypeAlert   = 1 << 2, 接受提醒(横幅/弹窗)
         */
        // iOS8需要添加请求用户的授权
        if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
            UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
            [application registerUserNotificationSettings:settings];
        }
    
        if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
            // 跳转界面
    
        }
    
        return YES;
    }
    
    /**
     *  如果应用在后台,通过点击通知的时候打开应用会来到该代理方法
     *  如果应用在前台,接受到本地通知就会调用该方法
     *
     *  @param notification 通过哪一个通知来这里
     */
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
        if (application.applicationState == UIApplicationStateActive) return;
        if (application.applicationState == UIApplicationStateInactive) {
    
            // 实现跳转
        }
    }
    
    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    {
        return YES;
    }



    #import "ViewController.h"
    
    @interface ViewController ()
    
    // 点击按钮之后添加通知
    - (IBAction)addLocalNote;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    }
    
    - (IBAction)addLocalNote {
    
        // 1.创建本地通知
        UILocalNotification *localNote = [[UILocalNotification alloc] init];
    
        // 设置什么时间弹出
        localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
    
        // 设置弹出的内容
        localNote.alertBody = @"您有新消息";
    
        // 设置锁屏状态下,显示的一个文字
        localNote.alertAction = @"快点打开";
    
    
        // 是否显示alertAction的文字(默认是YES)
        localNote.hasAction = YES;
    
        // 设置音效
        localNote.soundName = UILocalNotificationDefaultSoundName;
    
        // 应用图标右上角的提醒数字
        localNote.applicationIconBadgeNumber = 1;
    
        // 设置UserInfo来传递信息
        localNote.userInfo = @{@"alertBody" : localNote.alertBody, @"applicationIconBadgeNumber" : @(localNote.applicationIconBadgeNumber)};
    
        // 2.调度通知
        [[UIApplication sharedApplication] scheduleLocalNotification:localNote];
    }

    远程推送通知

    远程推送通知:就是通过网络从远程服务器推送给客户端的通知.

    为什么需要远程推送通知? 
    传统获取数据的局限性 
    只要用户关闭了app,就无法跟app的服务器沟通,无法从服务器上获得最新的数据内容

    远程推送通知可以解决以上问题 
    不管用户打开还是关闭app,只要联网了,都能接收到服务器推送的远程通知

    远程推送通知的使用 
    所有的苹果设备,在联网状态下,都会与苹果的服务器建立长连接 
    长连接: 只要联网了,就一直建立连接

    长连接的作用: 时间校准, 系统升级, 查找我的iPhone

    长连接的好处 : 数据传输速度快 , 数据保持最新状态

    远程推送功能机制

    苹果给iOS和Mac添加了消息推送的功能,使得我们可以通过后台服务器给应用程序(APP)发送消息,不管APP是否正在使用,比如邮箱的来件提示功能。这项服务被称为Apple Push Notification service(APNs)。里面一共涉及到四个角色:APP、设备、APNs和应用后台服务器(Provider),其中APP、后台服务器和APNs之间使用deviceToken唯一的标识一个用户。 
    这里写图片描述

    推送服务的工作流程:

    APP向系统注册推送服务。
    设备从APNs请求deviceToken。
    通过代理方法将deviceToken返回给APP。
    APP将deviceToken发送给应用后台服务器(Provider)。
    应用后台服务器保存deviceToken,然后在需要推送通知的时候,给APNs发送信息,使用deviceToken标识所要送达的客户端。
    APNs将后台服务器发过来的数据推送到设备。
    设备将消息分发给应用程序。
    
    在使用推送功能的时候,需要在开发者中心创建支持Push Notification的证书,并且将证书和私钥用于应用后台服务器与APNs之间通信。
    

    我也写了一个比较易懂的远程推送流程图! http://blog.csdn.net/ismilesky/article/details/48324723

    远程推送近年来,都是通过第三方进行实现,因为第三方推送的功能比较强大. 
    推送平台: 百度推送 , 极光推送, 腾讯信鸽推送, 个推 
    远程推送我们这里以极光推送(第三方)为例,进行测试.

    极光远程推送的使用

    如要使用极光第三方远程推送,我们需要集成iOS SDK,需要进行应用的配置,和环境配置.在开发者中心有 
    developer.apple.com开发者账号 , iOS真机(iPhone、iPad、iPod)等。

    远程推送应用配置过程:

    1. 创建支持远程推送功能的App ID
    2. 申请开发者证书,并选中刚刚创建的App ID
    3. 下载CER文件,并导入钥匙串管理
    4. 申请发布证书,并选中刚刚创建的App ID
    5. 下载CER文件,并导入钥匙串管理
    6. 检查App ID,确认证书已经指定

    这些相关配置极光推送已经给了非常详细的文档资料, iOS SDK集成指南 :http://docs.jpush.io/guideline/ios_guide/ , iOS SDK调试指南 :http://docs.jpush.io/client/ios_tutorials/#ios-sdk, 只需要按照流程进行就可以.

    实现代码:

    #define kDeviceVersion  ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
    
    #import "AppDelegate.h"
    #import "APService.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
    #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1
        if (kDeviceVersion) {
            //可以添加自定义categories
            [APService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:nil];
        } else {
            //categories 必须为nil
            [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];
        }
    #else
        //categories 必须为nil
        [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];
    
    #endif
    
        [APService setupWithOption:launchOptions];
    
    
        return YES;
    }
    
    #pragma mark - 获取device token (必须实现)
    // 当得到苹果的APNs服务器返回的DeviceToken就会被调用
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
        NSLog(@"%@",deviceToken);
        // Required
        [APService registerDeviceToken:deviceToken];
    }
    
    #pragma mark - 获取device token失败
    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
        NSLog(@"function == %s  line == %d  error == %@",__FUNCTION__,__LINE__,error);
    }
    
    // 接收到远程通知,触发方法和本地通知一致 (必须实现)
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
        // Required
        [APService handleRemoteNotification:userInfo];
    }
    
    #pragma mark - 使用后台的远程消息推送 (必须实现)
    /**
     1> 在Capabilities中打开远程推送通知
     2> 实现该代理方法
    
    远程消息数据格式:
     {"aps" : {"content-available" : 1},"content-id" : 42}
    
     执行completionHandler有两个目的
     1> 系统会估量App消耗的电量,并根据传递的UIBackgroundFetchResult 参数记录新数据是否可用
     2> 调用完成的处理代码时,应用的界面缩略图会自动更新
    
     注意:接收到远程通知到执行完网络请求之间的时间不能超过30秒
    
     if (userInfo) {
     int contentId = [userInfo[@"content-id"] intValue];
    
     ViewController *vc = (ViewController *)application.keyWindow.rootViewController;
     [vc loadDataWithContentID:contentId completion:^(NSArray *dataList) {
     vc.dataList = dataList;
    
     NSLog(@"刷新数据结束");
    
     completionHandler(UIBackgroundFetchResultNewData);
     }];
     } else {
     completionHandler(UIBackgroundFetchResultNoData);
     }
    
    */
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
        // IOS 7 Support Required
        [APService handleRemoteNotification:userInfo];
    
        [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];
    
        // 判断应用程序在前台还是后台
        if (application.applicationState == UIApplicationStateActive) { // 活跃状态
    
           // 实现方法
    
        } else if (application.applicationState == UIApplicationStateInactive) {
            // 不活跃状态
    
            // 实现方法
    
        } else { // application.applicationState == UIApplicationStateBackground
            // 后台
    
            // 实现方法
        }
        /** 必须回调 */
        completionHandler(UIBackgroundFetchResultNewData);
    
    }

    测试时,运行完应用程序,要发送通知,进行推送的测试,真机收到通知才算成功. 

    这里写图片描述

  • 相关阅读:
    ES6常用语法
    @Autowired和@Resource的区别
    spring boot 引导
    Springboot 日志管理配置logback-spring.xml
    Java异常之checked与unchecked
    @Transactional(rollbackFor=Exception.class)的使用
    使用spring @Scheduled注解执行定时任务、
    Spring入门学习推荐
    Hadoop调优
    关于Flink--ProcessFunction的使用
  • 原文地址:https://www.cnblogs.com/W-Kr/p/5422290.html
Copyright © 2011-2022 走看看