推 送 机 制
使用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应用中发送本地通知的步骤很简单,只要如下几步即可。
|
示例示范如何开发本地通知 |
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服务而言,完整的过程如下。
|
从上图可以看出,Push客户端应用需要3个组件。
Push服务端程序则需要如下两个组件
|
开发Push客户端应用
开发Push客户端应用需要到Apple网站注册一个App ID,而且该App ID不允许使用通配符。通过Apple网站注册App ID的步骤如下。
|
|
将证书签名请求文件保存到磁盘上,此处将该文件保存为“Push。cerSigningRequest”。
在上图所示页面中,可以看到在“iOS |
|
[BL1]本地通知与远程推送的区别
[BL2]开发iOSPush服务的完整的过程
[BL3]Push客户端应用需要3个组件