iOS中的推送机制详解

    使用NSNotificationCenter通信

    NSNotificationCenter实现了观察者模式,允许应用的不同对象之间以松耦合的方式进行通信。

    NSNotification代表Poster与Observer之间的信息载体,该对象包含如下只读属性。

    name:该属性代表该通知的名字,程序将Poster注册到指定通知中心时,就是根据该名称进行注册的
    object:该属性代表该通知的Poster。
    userInfo:该属性是一个NSDictionary对象,用于携带通知的附加信息。

    NSNotificationCenter是整个通知系统的中心,Observer向NSNotificationCenter注册自己感兴趣的通知,Poster向NSNotificationCenter发送通知。

    - addObserverForName:object:queue:usingBlock:该方法将指定代码块注册为监听者,监听object:参数代表的对象(Poster)发出的通知(由第1个参数指定通知名称)。该方法直接使用指定代码块作为监听者,当Poster向NSNotificationCenter发送通知时,将会触发、执行该代码块。如果object:参数为nil,则用于监听任何对象发出的通知。

    示例说明

     1 ViewController.m
     2 
     3 @implementation ViewController
     4 
     5 - (void)viewDidLoad
     6 
     7 {
     8 
     9             [super  viewDidLoad];
    10 
    11             // 监听UIApplicatiob的                  UIApplicationDidFinishLaunchingNotification通知
    12 
    13             [[NSNotificationCenter  defaultCenter]  addObserver:self
    14 
    15          selector:@selector(launch:)
    16 
    17          name:UIApplicationDidFinishLaunchingNotification
    18 
    19          object:[UIApplication sharedApplication]];
    20 
    21          // 监听UIApplicatiob的         UIApplicationDidEnterBackgroundNotification通知
    22 
    23           [[NSNotificationCenter  defaultCenter]  addObserver:self
    24 
    25          selector:@selector(back:)
    26 
    27          name:UIApplicationDidEnterBackgroundNotification
    28 
    29          object:[UIApplication sharedApplication]];
    30 
    31          // 监听UIApplicatiob的         UIApplicationWillEnterForegroundNotification通知
    32 
    33             [[NSNotificationCenter  defaultCenter]  addObserver:self
    34 
    35          selector:@selector(fore:)
    36 
    37          name:UIApplicationWillEnterForegroundNotification
    38 
    39          object:[UIApplication  sharedApplication]];
    40 
    41 }
    42 
    43 - (void)back: (NSNotification*)Notification
    44 
    45 {
    46 
    47             self.showLabel.text = [NSString stringWithFormat:@”应用程序加载完成!”];
    48 
    49 }
    50 
    51 - (void)launch: (NSNotification*)Notification
    52 
    53 {
    54 
    55             self.showLabel.text = [NSString  stringWithFormat:@”%@\n应用程序进入后台!”,
    56 
    57          self.showLabel.text];
    58 
    59 }
    60 
    61 - (void) fore: (NSNotification*)Notification
    62 
    63 {
    64 
    65          self.showLabel.text = [NSString  stringWithFormat:@”%@\n应用程序进入前台!”,
    66 
    67          self.showLabel.text];
    68 
    69 }
    70 
    71 @end

    使用NSNotificationCenter监听自定义通知

    使用NSNotificationCenter除了可以监听系统组件发出的通知之外,也可以监听程序自己发出的通知.下面示例将使用异步操作来模拟执行一个耗时任务,并在界面上使用UIProgressView显示耗时任务的执行进度.

    示例代码

    ViewController.m
    
    @import “ViewController.h”
    
    #define  PROGRESS_CHANGED   @”down_progress_changed”
    
    @interface ViewController()
    
    {
    
           NSNotificationCenter*  nc;
    
           NSOperationQueue* queue;
    
    }
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    
    {
    
              [super  viewDidLoad];
    
              nc = [NSNotificationCenter  defaultCenter];
    
              queue = [[NSOperationQueue  alloc]  init];
    
              // 设置该队列最多支持10个并发线程
    
              queue.maxConcurrentOperationCount = 10;
    
              // 使用视图控制器监听任何对象发出的 PROGRESS_CHANGED 通知
    
              [nc  addObserver:self  selector:@selector(update:)
    
           name: PROGRESS_CHANGED  object:nil]; // ①
    
    }
    
    - (IBAction)start: (id)sender
    
    {
    
           __block int progStatus = 0;
    
           [sender  setEnabled:NO];
    
           // 以传入的代码块作为执行体,创建NSOperation
    
           NSBlockOperation* operation = [NSBlockOperation 
    
           blockOperationWithBlock:^{
    
                    for(int I = 0; i < 100;  i++)
    
                   {
    
                      // 暂停0.5秒模拟耗时任务
    
                     [NSThread  sleepForTimeInterval: 0.5] ;
    
                   // 创建NSNotification,并指定userInfo信息
    
                           NSNotification*  noti = [NSNotification                notificationWithName: PROGRESS_CHANGED  object:nil  userInfo: [NSDictionary  dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: ++ progStatus]
    
    ,@”prog” ,  nil]];
    
                  // 发送通知
    
                   [nc  postNotification : noti]; // ②
    
                    }
    
           }];
    
           // 将NSOperation添加给NSOperationQueue
    
           [queue  addOperation:  operation];
    
    }
    
    - (void)update: (NSNotification*) noti
    
    {
    
           // 通过userInfo属性获取耗时任务的进度信息
    
           NSNumber* progStatus = noti.userInfo[@”prog”];
    
           NSLog(@”%d”, progStatus.intValue);
    
           dispatch_async(dispatch_get_main_queue(), ^{
    
              self.prog.progress = progStatus.intValue  /  100.0;
    
              // 当任务执行进度执行到100时,启用按钮
    
              if(100 == progStatus.intValue)
    
             {
    
                 [self.bn  setEnabled: YES];
    
              }
    
          });
    
    }
    
    @end
        上面程序中的第1行代码将视图控制器注册为通知监听者,用于监听任何对象的PROGRESS_CHANGED通知;接下来的第②行代码先创建了一个NSNotification,并使用NSNotificationCenter发送该通知。当该异步代码块向NSNotificationCenter发送通知之后,通知监听者的方法(update:)将会被触发执行,这个update:方法将会更新界面上UIProgressView的进度.

    iOS本地通知

    本地通知属于应用界面编程的内容,本地通知和远程推送通知都可以向不在前台运行的应用发送消息,这种消息既可能是即将发生的事件,也可以是服务器的新数据,都可能显示为一段警告信息信息或应用程序图标上的徽标。

    本地通知和远程推送通知的基本目的都死让应用程序能够通知用户某些事情,而且不需要应用程序在前台运行。二者的区别在于:本地通知由本应用负责调用,只能从当前设备上的 iOS 发出;而远程推送通知由远程服务器上的程序(可由任意语言编写)发送至 Apple Push Notification service(APNs), 再由 APNs 把消息推送至设备上对应的程序.[BL1]

     

    本地通知是一个 UILocalNotification对象,它有如下常用属性.
    fireData: 指定通知将在什么时间触发.
     repeatInterval:设置本地通知重复发送的时间间隔.
     alertBody: 设备本地通知的消息体.
     alertAction:  设置当设备处于锁屏状态时,显示通知的警告框下方的 title.
    has Action: 设置是否显示 Action.
    alertLaunchImage: 当用户通过该通知启动对应的应用时, 该属性设置为加载图片.
     applicatonIconBadgeNumber: 设置显示在应用程序上红色徽标中的数字.
     soundName: 设置通知的声音.
     userInfo: 设置该通知携带的附加信息.
    创建了 UILocalNotification对象之后,接下来就可以通过 UIApplication 的如下两个方法发送通知了
    l  - scheduleLocalNotification: 该方法指定调度通知。通知将会于 fireDate 指定的时间触发,而且会按 repeatInterval 指定的时间间隔重复触发。l  - presentLocalNotificationNow: 该方法指定立即发送通知。该方法会忽略UILocalNotification的fireDate属性。
    如果系统发出通知时,应用程序处于前台运行,系统将会触发应用程序委托类的application:didReceiveLocalNotification:方法。
    在iOS应用中发送本地通知的步骤很简单,只要如下几步即可。

    1. 创建UILocalNotification对象
    2. 设置UILocalNotification的属性
    3. 调用UIApplication的方法发送或调用通知
    4. 如果希望应用程序在前台运行时可以对通知进行相应的处理,则需要重写应用程序委托类的application:didReceiveLocalNotification:方法。
    5. 当应用需要取消本地通知时,可调用UIApplication的cancelLocalNotification:方法取消指定通知,或调用cancelLocalNotifications方法取消所有通知。

    示例示范如何开发本地通知

    ViewController.m
    
    @interface  ViewController()
    
    {
    
        UIApplication* app;
    
    }
    
    @end
    
    @implementation  ViewController
    
    - (void)viewDidLoad
    
    {
    
         [super  viewDidLoad];
    
        app = [UIApplication  sharedApplication];
    
    }
    
    - (IBAction)changed: (id)sender
    
    {
    
         UISwitch*  sw  = (UISwitch*)sender;
    
         if(sw.on)
    
         {
    
             //  创建一个本地通知
    
             UILocalNotification*  notification  = [[UILocalNotification  alloc] init]; // ①
    
             //  设置通知的触发时间
    
              notification.fireDate  = [NSDate  dateWithTimeIntervalSinceNow:10];
    
            //  设置通知的时区
    
           notification.timeZone = [NSTimeZone  defaultTimeZone];
    
           // 设置通知重复发送的时间间隔
    
           notification.repeatInterval = kCFCalendarUnitMinute;
    
              //  设置通知的声音
    
              notification.soundName  = @”gu.mp3”;
    
               //  设置当设备处于锁屏状态时, 显示通知的警告框下方的 title
    
               notification.alertAction =  @”打开”;
    
               //  设置通知是否显示 Action
    
               notification.hasAction = YES;
    
                //  设置通过通知加载应用时显示的图片
    
               notification.alertLaunchingImage  = @”logo.png”;
    
                  //  设置通知内容
    
                  notification.alertBody = @”轮到你下棋了,  赶快走棋!”;
    
                  // 设置显示在应用程序上红色徽标中的数字
    
                  notification.applicationIconBadgeNumber  =  1;
    
                  //  设置 userInfo, 用于携带额外的附加信息
    
                  NSDictionary*  info = @{@”123456”: @”key”};
    
                  notification.userInfo = info;
    
                  //  调度通知
    
                  [app scheduleLocalNotification:Notification]; // ①
    
                 }
    
           else
    
                  {
    
                      // 获取所有处于调度中的本地通知数组
    
                     NSArray*  localArray = [app  scheduledLocalNotifications];
    
                     if(localArray)
    
                     {
    
                          for(UILocalNotification * noti  in  localArray)
    
                           {
    
                             NSDictionary*  dict  = noti.userInfo;
    
                                if(dict)
    
                           {
    
                           //  如果找到要取消的通知
    
                           NSString*  inKey = [dict  objectForKey:@”key”];
    
                           if([inKey  isEqualToString:@”123456”])
    
                           {
    
                              // 取消调度该通知
    
                              [app  cancelLocalNotification: noti]; // ②
    
                             }
    
                           }
    
                         }
    
                       }
    
                 }
    
    }
    @end
       上面程序中的第①段代码创建了一个UILocalNotification对象,并为该对象设置了相关属性,接下来在①号代码处调用了UIApplication的scheduleLocalNotification:方法来调度通知,这样该通知将会在指定事件触发,并按相应的周期重复执行。当用户把UISwitch控件切换到关闭状态时,②号代码将会取消调度该通知。为了让程序处于前台运行时也能看到本地通知,还重写了应用程序委托类的application:didReceiveLocalNotification:方法。
     AppDelegate.m
    
    @implementation  AppDelegate
    //  只有当应用程序在前台运行时,该方法才会被调用
    
    - (void)application: (UIApplication*)application
    
      didReceiveLocalNotification: (UILocalNotification*) notification
    
    {
    
           // 如果应用程序在前台运行,则将应用程序图标上的红色徽标中的数字设为0
    
           application.applicationIconBadgeNumber = 0; // ①
    
           // 使用UIAlertView显示本地通知的信息
    
           [[[UIAlertView  alloc]  initWithTitle:@” 收到通知  ”
    
           message:notification.alertBody
    
           delegate:nil  cancelButtonTitle:@”确定”
    
           otherButtonTitles: nil]  show];  // ②
    
    }
    
    - (void)applicationWillEnterForeground: (UIApplication*) application
    
    {
    
                   // 当应用程序再次进入前台运行时,将应用程序徽标中数字设为0
    
                  application.applicationIconBadgeNumber = 0;
    
    }
    …
    @end
       上面程序中的第①②段代码控制当应用程序处于前台运行时,即使程序收到了本地通知,也依然会将应用程序图标上的红色徽标中数字设为0。

     iOS远程推送通知

    iOS远程推送通知由远程服务器上的程序(可由任意语言编写)发送至APNs,再由APNs把消息推送至设备上对应的程序。

    iOS远程推送通知的过程可用下图进行描述

     

    在上面中,Provider指远程服务器上的Push服务端应用,这种Push服务端应用可以使用任意语言编写,如Java、PHP等。

    APNs由Apple公司提供,APNs负责把通知发送到对应的iOS设备,该设备再把通知转发给ClientApp-----即我们的iOS应用。

    上面所示的过程分为3个阶段
    第一个阶段:Provider程序把要发送的通知、目标iPhone的device token (相当于该设备的唯一标识)打包,发给APNs.
    第二阶段:APNs通过已注册Push服务的iPhone列表查找具有对应device token的iPhone,并把Push通知发送给对应的iPhone。
    第三阶段:iPhone将收到的Push通知传递给相应的应用程序,并且按照设定弹出Push通知。
      实际上,Push服务端程序可以通过APNs将一条通知发送给多个iPhone 上的客户端应用;与此同时,iPhone上的客户端应用也可接收多个Push服务端程序发送过来的推送通知。下图显示了这种示意图

    对于开发iOSPush服务而言,完整的过程如下。

    1. 应用程序注册远程推送通知。
    2. 当应用程序注册推送通知成功或注册失败时,系统都会触发应用程序委托类的对应方法。可以通过重写应用程序委托类的这些方法来获取该设备注册成功后得到的device token。
    3. 应用程序将device token发送给Push服务端程序
    4. 服务端程序向APNs发送通知。
    5. APNs将通知发送给iOS应用------实际的过程是先发送给指定的iOS设备,再由该设备弹出Push通知。[BL2]

    从上图可以看出,Push客户端应用需要3个组件。

    • App ID(应用程序唯一标识,这个必须到Apple网站注册来获得)
    • Provisioning Profile(这个也必须到Apple网站下载)
    • device token(当Push客户端应用注册Push推送通知成功时,APNs将会返回该设备的device  token)。[BL3]

    Push服务端程序则需要如下两个组件

    • SSL Certificate(SSL连接证书,这个必须从Apple网站下载)。
    • Private Key(私钥,这个可用过开发者电脑导出)。

    开发Push客户端应用

    开发Push客户端应用需要到Apple网站注册一个App ID,而且该App ID不允许使用通配符。通过Apple网站注册App ID的步骤如下。

    1. 打开OS X系统上的”钥匙串访问”应用,单击该应用的主菜单”钥匙串访问” →”证书助理”→”从证书颁发机构请求证书”,如下图所示.

    1. 单击”从证书颁发机构请求证书”后,将会显示下图所示的对话框
    2. 输入电子邮件地址和常用名称,并选中“存储到磁盘”单选钮,然后单击“继续”按钮,该程序将会创建一个“Certificate Signing Request”(证书签名请求)文件,系统弹出下图所示的保存文件对话框
    将证书签名请求文件保存到磁盘上,此处将该文件保存为“Push。cerSigningRequest”。

    1. 使用浏览器打开 https://developer.apple.com/ios/manage/overview/index.action站点, 页面上将会提示用户输入开发者账号\密码. 登录成功后会看到如下说是的页面

    在上图所示页面中,可以看到在“iOS Apps”栏目下包含了Certificates、Identifiers、Device、ProvisioningProfiles-----这些只有登录账号已经加入iOS Developer Program(iDP)的原因。

    1. 单击“iOS Apps” →“Identifiers”→栏目下的”App IDS” 链接,系统将会显示如下图

     


     [BL1]本地通知与远程推送的区别

     [BL2]开发iOSPush服务的完整的过程

     [BL3]Push客户端应用需要3个组件

    本博客所有文章如无特别注明均为原创。
    复制或转载请以超链接形式注明转自koala,原文地址《iOS中的推送机制详解
    喜欢 | 0
    分享:

还没有人抢沙发呢~