當(dāng)系統(tǒng)通知欄接收到JPush遠(yuǎn)程推送消息,并實(shí)現(xiàn)點(diǎn)擊消息跳轉(zhuǎn)指定頁面,處理推送消息的回調(diào)函數(shù)如下:
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
NSDictionary * userInfo = response.notification.request.content.userInfo;
UNNotificationRequest *request = response.notification.request; // 收到推送的請(qǐng)求
UNNotificationContent *content = request.content; // 收到推送的消息內(nèi)容
NSNumber *badge = content.badge; // 推送消息的角標(biāo)
NSString *body = content.body; // 推送消息體
UNNotificationSound *sound = content.sound; // 推送消息的聲音
NSString *subtitle = content.subtitle; // 推送消息的副標(biāo)題
NSString *title = content.title; // 推送消息的標(biāo)題
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
if (userInfo) {
[self push:userInfo];
}else{
return;
}
[JPUSHService handleRemoteNotification:userInfo];
}
else {
}
completionHandler(); // 系統(tǒng)要求執(zhí)行這個(gè)方法
}
一顽照、我們需要分析有哪些跳轉(zhuǎn)的情景:
1、程序在前臺(tái)運(yùn)行時(shí)接收到消息
2闽寡、程序在后臺(tái)運(yùn)行時(shí)接收到消息
3代兵、程序處于終止(殺死)狀態(tài)下接收到消息
那么本文重點(diǎn)來探討下當(dāng)程序處于終止?fàn)顟B(tài)下時(shí)接收到消息如何處理?
不設(shè)懸念直入主題吧爷狈,仿照QQ植影、微信等其他APP的推送機(jī)制可以了解到,當(dāng)程序被終止?fàn)顟B(tài)下涎永,點(diǎn)擊通知欄的消息只打開app并不能跳轉(zhuǎn)特定頁面思币,原因是appDelegate的main函數(shù)不執(zhí)行,那么Push的載體導(dǎo)航器也不存在羡微。系統(tǒng)只能根據(jù)通知欄所點(diǎn)擊消息對(duì)應(yīng)遠(yuǎn)程推送注冊(cè)碼來選擇啟動(dòng)哪一個(gè)APP.
所以消息處理的邏輯即:
1谷饿、程序未被終止?fàn)顟B(tài)下,編寫正常跳轉(zhuǎn)特定頁面的代碼
2妈倔、程序被終止?fàn)顟B(tài)下博投,屏蔽Push跳轉(zhuǎn)代碼,只啟動(dòng)app即可
二盯蝴、問題來了毅哗,進(jìn)程被終止回調(diào)機(jī)制是什么呢听怕?
1、程序進(jìn)程被終止會(huì)調(diào)用此函數(shù)
// 程序進(jìn)程被終止時(shí)調(diào)用
- (void)applicationWillTerminate:(UIApplication *)application{
[[NSUserDefaults standardUserDefaults] setObject:applicationWillTerminate forKey:applicationWillTerminate];
[[NSUserDefaults standardUserDefaults] synchronize];
}
需要注意:此回調(diào)函數(shù)不能主動(dòng)被調(diào)起虑绵,還需要在程序進(jìn)入后臺(tái)的回調(diào)函數(shù)中開啟UIBackgroundTaskIdentifier
任務(wù)尿瞭,即如下操作
2、聲明全局變量
UIBackgroundTaskIdentifier _bgTask;
3翅睛、開啟后臺(tái)任務(wù)
- (void)applicationDidEnterBackground:(UIApplication *)application {
_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
// Synchronize the cleanup call on the main thread in case
// the task actually finishes at around the same time.
dispatch_async(dispatch_get_main_queue(), ^{
if (_bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:_bgTask];
_bgTask = UIBackgroundTaskInvalid;
}
});
}];
}
亦可在程序進(jìn)入后臺(tái)執(zhí)行一些保存声搁、清理操作
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(){
//程序在10分鐘內(nèi)未被系統(tǒng)關(guān)閉或者強(qiáng)制關(guān)閉,則程序會(huì)調(diào)用此代碼塊宏所,可以在這里做一些保存或者清理工作
NSLog(@"程序關(guān)閉");
}];
三酥艳、如何判斷當(dāng)前的程序是否已經(jīng)被終止呢?
顯然普通的BOOL或者屬性記錄是行不通的爬骤,appDelegate的代碼就不執(zhí)行充石,那么找到的解決方案是:輕量級(jí)存儲(chǔ)。
1霞玄、定義常量
// 定義當(dāng)前程序被終止常量
static NSString *const applicationWillTerminate = @"applicationWillTerminate";
2骤铃、記錄進(jìn)程被強(qiáng)制終止
// 程序進(jìn)程被終止時(shí)調(diào)用
- (void)applicationWillTerminate:(UIApplication *)application{
[[NSUserDefaults standardUserDefaults] setObject:applicationWillTerminate forKey:applicationWillTerminate];
[[NSUserDefaults standardUserDefaults] synchronize];
}
3、當(dāng)已啟動(dòng)進(jìn)程時(shí)remove掉存儲(chǔ)對(duì)象坷剧。
[[NSUserDefaults standardUserDefaults] removeObjectForKey:applicationWillTerminate];
[[NSUserDefaults standardUserDefaults] synchronize];
四惰爬、runtime處理推送消息跳轉(zhuǎn)特定頁面源碼
- (void)push:(NSDictionary *)userInfo
{
NSDictionary *params;
if ([[NSString stringWithFormat:@"%@",userInfo[@"aps"][@"badge"]] isEqualToString:@"1"]) {
//邀請(qǐng)面試
params = @{
@"class": @"InterviewTimeViewController",
@"property": @{
@"ID": @"123",
@"channelType": @"12"
}
};
}else if ([[NSString stringWithFormat:@"%@",userInfo[@"extrasKey"]] isEqualToString:@"0"]){
//查看簡歷狀態(tài)
params = @{
@"class": @"ResumeStateViewController",
@"property": @{
@"ID": @"234",
@"channelType": @"13"
}
};
}
// 類名
NSString *classStr =[NSString stringWithFormat:@"%@", params[@"class"]];
const char *className = [classStr cStringUsingEncoding:NSASCIIStringEncoding];
// 從一個(gè)字串返回一個(gè)類
Class newClass = objc_getClass(className);
if (!newClass)
{
// 創(chuàng)建一個(gè)類
Class superClass = [NSObject class];
newClass = objc_allocateClassPair(superClass, className, 0);
// 注冊(cè)你創(chuàng)建的這個(gè)類
objc_registerClassPair(newClass);
}
// 創(chuàng)建對(duì)象
id instance = [[newClass alloc] init];
// 對(duì)該對(duì)象賦值屬性
NSDictionary * propertys = params[@"property"];
[propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
// 檢測(cè)這個(gè)對(duì)象是否存在該屬性
if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {
// 利用kvc賦值
[instance setValue:obj forKey:key];
}
}];
NSString *terminate = [[NSUserDefaults standardUserDefaults] objectForKey:applicationWillTerminate];
if (![terminate isEqualToString:applicationWillTerminate]) {
// 獲取導(dǎo)航控制器
UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;
UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];
// 跳轉(zhuǎn)到對(duì)應(yīng)的控制器
[pushClassStance pushViewController:instance animated:YES];
}
}
- (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName
{
unsigned int outCount, i;
// 獲取對(duì)象里的屬性列表
objc_property_t * properties = class_copyPropertyList([instance
class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property =properties[i];
// 屬性名轉(zhuǎn)成字符串
NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
// 判斷該屬性是否存在
if ([propertyName isEqualToString:verifyPropertyName]) {
free(properties);
return YES;
}
}
free(properties);
return NO;
}