本章將會詳細介紹iOS本地通知和遠程通知相關的知識。本地通知和遠程推送通知都可以向不在前台運行的應用發送消息,他們在程序界面上的顯示效果相同,都可能顯示為一段警告信息或應用程序圖標上的徽標。
不管是本地通知還是遠程推送通知,都可對用戶進行提醒,提醒用戶即將要做的事情,也可將服務器數據發送給iOS客戶端。本地通知和推送通知的基本目的都是讓應用程序能夠通知用戶某些事情,而且不需要應用程序在前台運行。
NSNotificationCenter實現了觀察者模式,允許應用的不同對象之間以松耦合的方式進行通信。NSNotificationCenter就是iOS SDK為開發者實現的觀察者模式。這種設計模式的示意圖如下:
NSNotificationCenter相當於一個『消息中心』,首先由Observer組件向NSNotificationCenter進行注冊——表明該Observer組件對哪些NSNotification感興趣。在咩有組件發送NSNotification的情況下,這些組件都處於靜止狀態,不會執行任何代碼。
當Poster向NSNotificationCenter發送NSNotification後,所有在NSNotificationCenter上注冊過、對該NSNotification感興趣的Observer都會被激發。
NSNotification代表Poster與Pbserver之間的信息載體,該對象包含如下只讀屬性:
//①、
@property (readonly, copy) NSString *name;
//該屬性代表該通知的名字。程序將Poster注冊到指定通知中心時,就是根據該名稱進行注冊的。
//②、
@property (nullable, readonly, retain) id object;
//該屬性代表該通知的Poster。
//③、
@property (nullable, readonly, copy) NSDictionary *userInfo;
//該屬性該表是一個NSDictionary對象,用於攜帶通知的附加信息。
NSNotificationCenter是整個通知系統的中心,Observer向NSNotificationCenter注冊自己感興趣的通知,Poster向NSNotificationCenter發送通知。
NSNotificationCenter提供了defaultCenter類方法獲取系統默認的通知中心對象,獲取NSNotificationCenter對象之後,接下來即可通過如下方法注冊和刪除監聽者。
①、
- (id )addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
//該方法將指定代碼塊注冊為監聽者,監聽object:參數代表的對象(Poster)發出的通知(由第1個參數指定通知名稱)。該方法直接使用指定代碼塊作為監聽者,當Poster向NSNotificationCenter發送通知時,將會觸發、執行該代碼塊。如果object:參數為nil,則用於監聽任何對象發出的通知。
②、
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString *)aName object:(nullable id)anObject;
//該方法將第1個參數注冊為監聽者,監聽bject:參數代表的對象(Poster)發出的通知(由name:參數指定通知名稱),其中selector:參數用於指定監聽者的監聽方法——當Poster向NSNotificationCenter發送通知時,將會觸發、執行object:參數代表的對象的selector方法。
③、
- (void)removeObserver:(id)observer;
//刪除一個監聽者;
④、
- (void)removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject;
//取消監聽者對指定Poster、指定通知的監聽;
⑤、
- (void)postNotification:(NSNotification *)notification;
//發送通知。該方法需要一個NSNotification對象作為通知;
⑥、
- (void)postNotificationName:(NSString *)aName object:(nullable id)anObject;
//發送通知。該方法的第1個參數指定通知名稱,第2個參數指定通知的Poster。
⑦、
- (void)postNotificationName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
//發送通知。該方法的第1個參數指定通知名稱,第2個參數指定通知的Poster,第3個參數指定通知的userInfo。
- (void)viewDidLoad {
[super viewDidLoad];
//監聽UIAlication的UIApplicationDidFinishLaunchingNotification通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(lauch:) name:UIApplicationDidFinishLaunchingNotification object:[UIApplication sharedApplication]];
//監聽UIAlication的UIApplicationDidEnterBackgroundNotification通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(back:) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];
//監聽UIAlication的UIApplicationWillEnterForegroundNotification通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(fore:) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
}
- (void)lauch:(NSNotification *)notification{
NSLog(@"應用程序加載完成!");
}
- (void)back:(NSNotification *)notification{
NSLog(@"應用程序進入後台!");
}
- (void)fore:(NSNotification *)notification{
NSLog(@"應用程序進入前台!");
}
#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];
[self start];
}
- (void)start{
__block int progStatus = 0;
//以傳入的代碼快作為執行體,創建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 *)notification{
//userInfo屬性獲取耗時任務的進度信息
NSNumber* progStatus = notification.userInfo[@"prog"];
NSLog(@"%d",progStatus.intValue);
dispatch_async(dispatch_get_main_queue(), ^{
//做一個視圖改變
});
}
@end
本地通知和遠程推送通知不同於前面介紹的NSNotification,這種通知屬於應用界面編程的內容,本地通知和遠程推送通知都可以向不在前台運行的應用發送消息,這種消息即可能是即將發生的事件,也可能是服務器的新數據。不管是本地通知還是遠程推送通知,它們在程序界面的顯示效果相同,都可能顯示一段警告信息或應用程序圖標上的徽標。
當用戶點擊本地通知或遠程推送通知時,可以啟動相應的應用程序來查看詳情,也可以選擇忽略通知,此時應用程序將不會被激活。
本地通知和遠程推送通知的基本目的都是讓應用程序能夠通知用戶某些事情,而且不需要應用程序在前台運行。二者的區別在於:本地通知由本應用負責調用,只能從當前設備上iOS發出;而遠程推送通知由遠程服務器上的程序(可由任意語言編寫)發送至Apple Push Notification service(APNs),再由APNs把消息推送至設備上對應的程序。
本地通知(僅在iOS中有效)適用於基於時間的程序,包括簡單的日歷程序或者to-do列表類型的應用程序。那些在有限時間內運行的後台程序(iOS系統許可的)也能接收到本地通知。例如:需要從服務端獲取數據的應用程序,可以在後台運行並查詢服務端最新的數據,如果有消息或數據需要通知用戶更新或下載,程序可以立即發送一個本地通知來通知用戶。
本地通知是一個UILocalNotification對象,它有如下常用屬性:
①、
@property(nullable, nonatomic,copy) NSDate *fireDate;
//指定通知將在什麼時間觸發。
②、
@property(nonatomic) NSCalendarUnit repeatInterval;
//設置本地通知重復發送的時間間隔;
③、
@property(nullable, nonatomic,copy) NSString *alertBody;
//設置本地通知的消息體;
④、
@property(nullable, nonatomic,copy) NSString *alertAction;
//設置當設備處於鎖屏狀態時,顯示通知的警告框下方的title;
⑤、
@property(nonatomic) BOOL hasAction;
//設置是否顯示Action;
⑥、
@property(nullable, nonatomic,copy) NSString *alertLaunchImage;
//當用戶通過該通知啟動對應的應用時,該屬性設置為加載圖片;
⑦、
@property(nonatomic) NSInteger applicationIconBadgeNumber;
//設置顯示在應用程序上紅色徽標中的數字;
⑧、
@property(nullable, nonatomic,copy) NSString *soundName;
//設置通知的聲音;
⑨、
@property(nullable, nonatomic,copy) NSDictionary *userInfo;
//設置該通知攜帶的附加信息;
創建了UILocalNotification對象之後,接下來就可以通過UIApplication的如下兩個方法發送通知了:
①、
- (void)scheduleLocalNotification:(UILocalNotification *)notification NS_AVAILABLE_IOS(4_0) __TVOS_PROHIBITED; // copies notification
//該方法指定調度通知。通知將會在於fireDate指定的時間觸發,而且會按repeatInterval指定的時間間隔重復觸發。
②、
- (void)presentLocalNotificationNow:(UILocalNotification *)notification NS_AVAILABLE_IOS(4_0) __TVOS_PROHIBITED;
//該方法指定立即發送通知。該方法忽略UILocalNotification的fireDate屬性。
每個應用程序最多只能發送最近(新)的64個本地通知,超過該限制的通知將被操作系統自動放棄。重復出現的通知會被認為是一個通知。
除此之外,如果系統發出通知時,應用程序處於前台運行,系統將會觸發應用程序委托類的方法
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification NS_AVAILABLE_IOS(4_0) __TVOS_PROHIBITED;
在iOS應用中發送本地通知的步驟如下:
創建UILocalNotification對象; 設置UILocalNotification的屬性; 調用UIApplication的方法發送或調用通知; 如果希望應用程序在前台運行時可以對通知進行相應的處理,則需要重寫應用程序委托類application:didReceiveLocalNotification:方法; 當應用需要取消本地通知,可調用UIApplication的cancelLocalNotification:方法取消指定通知,或調用cancelLocalNotifications方法取消所有通知。例如:
@interface ViewController ()
{
UIApplication* app;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
app = [UIApplication sharedApplication];
UISwitch* uiSwitch = [[UISwitch alloc]initWithFrame:CGRectMake(100, 100, 100, 40)];
[uiSwitch addTarget:self action:@selector(clickSwitch:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:uiSwitch];
}
- (void)clickSwitch:(UISwitch *)sender{
if (sender.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.alertLaunchImage = @"logo.png";
//設置通知內容
notification.alertBody = @"唉,我很尴尬你知道不";
//設置顯示在應用程序上紅色徽標中的數字
notification.applicationIconBadgeNumber = 1;
//設置userInfo,用於攜帶額外的附加信息
NSDictionary* info = @{@"key":@"fkjava.org"};
notification.userInfo = info;
//調度通知
[app scheduleLocalNotification:notification];
}else{
//獲取所有處於調度中的本地通知數組
NSArray* localArray = [app scheduledLocalNotifications];
NSLog(@"%@",localArray);
if (localArray) {
for (UILocalNotification* noti in localArray) {
[app cancelLocalNotification:noti];
}
}
//取消所有的通知
[app cancelAllLocalNotifications];
}
}
@end
//只有當應用程序在前台運行時,該方法才會被調用
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
//如果應用程序在前台運行,則將應用程序圖標上的紅色徽標中數字設為0
application.applicationIconBadgeNumber = 0;
NSLog(@"收到通知:%@",notification.alertBody);
}
iOS遠程推送通知由遠程服務器上的程序(可由任意語言編寫)發送至APNs,再由APNs把消息推送至設備上對應的程序。
iOS遠程推送通知的過程如下:
Provider指遠程服務器上的Push服務端應用,這種Push服務端應用可以使用任何語言編寫,如Java,PHP等。APNs由Apple公司提供,APNs負責把通知發送到對應的iOS設備,該設備再把通知轉發給Client App——即我們的iOS應用。
該過程可分為3個階段:
第一階段:Provider程序把要發送的通知、目標iPhone的device token(相當於該設備的第一標識)打包,發送給APNs。 第二階段:APNs通過已注冊Push服務iPhone列表查找具有對應的device token的iPhone,並把Push通知發送給對應的iPhone。 第三階段:iPhone將收到的Push通知傳遞給相應的應用程序,並且按照設定彈出Puch通知。(1)、Push客戶端應用需要3個組件<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCkFwcCBJRKOo06bTw7PM0PLOqNK7serKtqOs1eK49rHY0Ou1vUFwcGxlzfjVvteisuHAtLvxtcOjqaO7IFByb3Zpc2lvbmluZyBQcm9maWxlo6i1vUFwcGxlzfjVvs/C1NijqaO7IGRldmljZSB0b2tlbqOotbFQdXNov827p7bL06bTw9eisuFQdXNozcbLzc2o1qqzybmmyrGjrEFQTnO9q7vht7W72LjDyeixuGRldmljZSB0b2tlbqOpo7sNCjxwPqOoMqOpoaJQdXNot/7O8bbLs8zQ8tDo0qrI58/Cwb249tfpvP48L3A+DQpTU0wgQ2VydGlmaWNhdGUgU1NMway909akyumjrNXiuPax2NDrtNNBcHBsZc341b7PwtTYo7sgUHJpdmF0ZSBLZXmjqMu91L+jrNXiuPa/yc2ouf2/qrei1eK158TUtbyz9qOpo7sNCjxoMSBpZD0="1開發push客戶端應用">1、開發Push客戶端應用
開發Push客戶端應用需要到Apple網站注冊一個App ID,而且該App ID不允許使用配符。通過Apple網站注冊App ID的步驟如下。
①、打開OS X系統上的『鑰匙串訪問』應用,單擊該應用的主菜單『鑰匙串訪問』->『證書助理』->『從證書頒發機構請求證書』,如下圖
②、單擊『從證書發布機構申請證書』將會出現如圖的對話框
③、輸入電子郵件地址和常用名稱,並選中『存儲到磁盤』單選項,然後單擊『繼續』按鈕,該程序將會創建一個『Certificate Signing Request(證書簽名請求)』文件,系統彈出如圖所示的保存文件對話框;
將證書簽名請求文件保存到磁盤上,此處將該文件保存為『Push.certSigningRequest』。
④、使用浏覽器打開https//:developer.apple.com/ios/manage/overview/index.action站點,頁面上將會提示用戶輸入開發者帳號、密碼。登錄成功將會看到如下所示的頁面(現在已改版,大體差不多)
⑤、單擊『iOS App』->『Identifiers』欄目下的『App IDs』鏈接,系統將會顯示如下
⑥、點擊頁面右上角的『+』按鈕(添加App ID),系統打開如下頁面:
對於Push通知而言,App ID不能帶通配符的ID,因此在上圖中選擇『Explicit App ID』分類,按圖所示輸入App ID的描述字符串和唯一標識。需要說明的是,App ID的描述字符串可以隨便填,但該App ID的唯一標識必須要記住,通常采用『公司域名+應用名』的格式。
填寫完成後,單擊頁面下方的『Continue』按鈕。
⑦、單擊剛剛注冊的App ID,可以看到該App ID支持的各種服務,如下:
⑧、從上圖所示的頁面可以看出,該App ID病不支持Push Notifications(推送通知)和iCloud服務。單擊『Edit』按鈕,進入編輯該App ID的頁面:
⑨、勾選Push Notifications右邊的復選框——這回啟用該App ID的Push通知功能。勾選該復選框之後,Push Notification下面的兩個『Create Certificate…』按鈕將會變成可用狀態,其中第1個按鈕用於創建開發階段的證書,第2個按鈕用於創建產品化階段的證書。此處只需要創建開發階段的證書,當需要正式發布該iOS應用時,還需要創建產品化階段的證書。
⑩、單擊第1個『Create Certificate』按鈕,系統將會顯示一個提示頁面,告訴用戶去創建一個CSR文件——也就是我們前面已經創建的『Certificate Signing Request』文件,由於我們已經創建了該文件,因此直接單擊『Continue』按鈕,系統會顯示如圖頁面:
?、單擊『Choose File』按鈕選擇前面創建的Push.certSigningRequest文件,然後單擊『Generate』按鈕,系統將會為該App ID生成開發證書。成功生成開發證書之後,可以看到如下頁面:
?、成功生成App ID的開發證書之後,即可通過該頁面中的『Download』按鈕下載證書,也可以返回系統證書列表也米娜去下載證書。此處先將下載並保存到本地磁盤,該證書的文件名為『apd_development.cer』。
?、雙擊上一步下載得到的『apd_development.cer』文件,OS X系統將會自動打開『鑰匙串訪問』應用程序,並將該證書添加到系統中。此時將可以在『鑰匙串訪問』應用程序中看到如下一行:
?、通過證書列表頁面也可以下載指定的證書(只要點擊指定的證書,頁面就會顯示『Revoke』、『Download』兩個按鈕,其中『Revoke』按鈕用於刪除證書,『Download』按鈕用於下載證書)。
經過上面步驟,我們已經成功為Push客戶端創建一個支持Push通知的App ID,並下載,安裝了該App ID的開發證書。
現在開始開發Push客戶端應用。新建一個Single View Application,該應用的『Bundle Identifier』必須與前面注冊的App ID完全相同。
接下來通過修改應用程序委托類來注冊遠程Push通知,並重寫對應的方法來處理遠程Push通知。
UIApplication提供了如下方法來注冊遠程Push通知:
①、
- (void)registerForRemoteNotificationTypes:(UIRemoteNotificationType)types NS_DEPRECATED_IOS(3_0, 8_0, "Please use registerForRemoteNotifications and registerUserNotificationSettings: instead") __TVOS_PROHIBITED;
//注冊遠程推送通知iOS8後由registerForRemoteNotifications和registerUserNotificationSettings:代替;
②、
- (void)unregisterForRemoteNotifications NS_AVAILABLE_IOS(3_0);
//取消注冊遠程推送通知;
UIApplicationDelegate中則定義了如下與遠程推送通知相關方法:
①、
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo NS_AVAILABLE_IOS(3_0);
//應用程序收到遠程Push通知時自動執行該方法;
②、
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken NS_AVAILABLE_IOS(3_0);
//該應用成功注冊遠程推送通知時激發該方法;
③、
- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error NS_AVAILABLE_IOS(8_0);
//該應用注冊遠程推送通知失敗時激發該方法;
iOS應用處理遠程通知比較簡單:
①、在應用程序委托類的application:didFinishLaunchingWithOptions:方法中調用UIApplication的registerForRemoteNotificationTypes:注冊遠程Push通知(iOS 8以後調用registerForRemoteNotifications和registerUserNotificationSettings:) ②、重寫應用程序委托類的如上3個方法。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#ifdef __IPHONE_8_0
//IOS8 新的通知機制category注冊
UIUserNotificationType types = (UIUserNotificationTypeAlert|
UIUserNotificationTypeSound|
UIUserNotificationTypeBadge);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerForRemoteNotifications];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
#else
UIRemoteNotificationType apn_type = (UIRemoteNotificationType)(UIRemoteNotificationTypeAlert|
UIRemoteNotificationTypeSound|
UIRemoteNotificationTypeBadge);
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:apn_type];
#endif
// Override point for customization after application launch.
return YES;
}
#pragma mark -- UIApplicationDelegate
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken{
NSLog(@"注冊成功:%@", pToken);
//注冊成功,應該將該device token 發送到Push服務端程序,
//Push服務端程序應該將該Token保存到數據庫中,以備以後重復使用
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(@"注冊失敗:%@",error);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
NSLog(@"收到通知:userInfo == %@",userInfo);
NSString *message = [[userInfo objectForKey:@"aps"]objectForKey:@"alert"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定",nil];
[alert show];
}