今天遇到了一个程序停止的问题:

=================================

当系统通知栏接收到JPush远程推送消息,并实现点击消息跳转指定页面,处理推送消息的回调函数如下:

应用程序: BussinessService.exe Framework 版本: v4.0.30319 说明:
由于未经处理的异常,进程终止。
异常信息: System.InvalidOperationException 在……

TaskList:         列出当前所有运行进程。
        使用方法:在命令提示符中输入tasklist
然后回车,会看到类似下面的列表:
映像名称                       PID 会话名              会话#      
内存使用

- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {

    NSDictionary * userInfo = response.notification.request.content.userInfo;
    UNNotificationRequest *request = response.notification.request; // 收到推送的请求
    UNNotificationContent *content = request.content; // 收到推送的消息内容
    NSNumber *badge = content.badge;  // 推送消息的角标
    NSString *body = content.body;    // 推送消息体
    UNNotificationSound *sound = content.sound;  // 推送消息的声音
    NSString *subtitle = content.subtitle;  // 推送消息的副标题
    NSString *title = content.title;  // 推送消息的标题

    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        if (userInfo) {
            [self push:userInfo];
        }else{
            return;
        }
        [JPUSHService handleRemoteNotification:userInfo];
    }
    else {
    }
    completionHandler();  // 系统要求执行这个方法
}

 意外的报错,程序本身没有日志记录下来,这时候可以到“计算机管理”——“windows日志”——根据时间及来源定位你的程序报错的日志——“详细信息”

========================= ======== ================ ===========

System Idle Process              0 Console                    0        
28 K
System                           4 Console                    0       
328 K
smss.exe                       376 Console                    0       
500 K

Tskill:tskill
PID
        
结束某个进程,比如我想结束上面的Maxthom,就可以这样:
Tskill 6076
        其中的6076为Maxthon的ID。

有兴趣的同学可以下载试一下这个软件和上面的命令(提示:要ALT+ENTER全屏才行)

     3,784 K
inetinfo.exe                  3652 Console                    0     
9,272 K

Tskill:tskill
PID
        
结束某个进程,比如我想结束上面的Maxthom,就可以这样:
Tskill 6076
        其中的6076为Maxthon的ID。

有兴趣的同学可以下载试一下这个软件和上面的命令(提示:要ALT+ENTER全屏才行)

一、我们需要分析有哪些跳转的情景:

1、程序在前台运行时接收到消息
2、程序在后台运行时接收到消息
3、程序处于终止(杀死)状态下接收到消息

那么本文重点来探讨下当程序处于终止状态下时接收到消息如何处理?

不设悬念直入主题吧,仿照QQ、微信等其他APP的推送机制可以了解到,当程序被终止状态下,点击通知栏的消息只打开app并不能跳转特定页面,原因是appDelegate的main函数不执行,那么Push的载体导航器也不存在。系统只能根据通知栏所点击消息对应远程推送注册码来选择启动哪一个APP.

所以消息处理的逻辑即:

1、程序未被终止状态下,编写正常跳转特定页面的代码
2、程序被终止状态下,屏蔽Push跳转代码,只启动app即可

图片 1

二、问题来了,进程被终止回调机制是什么呢?

1、程序进程被终止会调用此函数

// 程序进程被终止时调用
- (void)applicationWillTerminate:(UIApplication *)application{
    [[NSUserDefaults standardUserDefaults] setObject:applicationWillTerminate forKey:applicationWillTerminate];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

需要注意:此回调函数不能主动被调起,还需要在程序进入后台的回调函数中开启UIBackgroundTaskIdentifier任务,即如下操作

2、声明全局变量

UIBackgroundTaskIdentifier _bgTask;

3、开启后台任务

- (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;
            }
        });
    }];
}

亦可在程序进入后台执行一些保存、清理操作

[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(){
//程序在10分钟内未被系统关闭或者强制关闭,则程序会调用此代码块,可以在这里做一些保存或者清理工作
NSLog(@"程序关闭");
}];

 

三、如何判断当前的程序是否已经被终止呢?

显然普通的BOOL或者属性记录是行不通的,appDelegate的代码就不执行,那么找到的解决方案是:轻量级存储。

1、定义常量

// 定义当前程序被终止常量
static NSString *const applicationWillTerminate = @"applicationWillTerminate";

2、记录进程被强制终止

// 程序进程被终止时调用
- (void)applicationWillTerminate:(UIApplication *)application{
    [[NSUserDefaults standardUserDefaults] setObject:applicationWillTerminate forKey:applicationWillTerminate];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

3、当已启动进程时remove掉存储对象。

    [[NSUserDefaults standardUserDefaults] removeObjectForKey:applicationWillTerminate];
    [[NSUserDefaults standardUserDefaults] synchronize];
四、runtime处理推送消息跳转特定页面源码
- (void)push:(NSDictionary *)userInfo
{
    NSDictionary *params;
    if ([[NSString stringWithFormat:@"%@",userInfo[@"aps"][@"badge"]] isEqualToString:@"1"]) {
        //邀请面试
        params = @{
                   @"class": @"InterviewTimeViewController",
                   @"property": @{
                           @"ID": @"123",
                           @"channelType": @"12"
                           }
                   };
    }else if ([[NSString stringWithFormat:@"%@",userInfo[@"extrasKey"]] isEqualToString:@"0"]){
        //查看简历状态
        params = @{
                   @"class": @"ResumeStateViewController",
                   @"property": @{
                           @"ID": @"234",
                           @"channelType": @"13"
                           }
                   };
    }

    // 类名
    NSString *classStr =[NSString stringWithFormat:@"%@", params[@"class"]];
    const char *className = [classStr cStringUsingEncoding:NSASCIIStringEncoding];

    // 从一个字串返回一个类
    Class newClass = objc_getClass(className);
    if (!newClass)
    {
        // 创建一个类
        Class superClass = [NSObject class];
        newClass = objc_allocateClassPair(superClass, className, 0);
        // 注册你创建的这个类
        objc_registerClassPair(newClass);
    }
    // 创建对象
    id instance = [[newClass alloc] init];

    // 对该对象赋值属性
    NSDictionary * propertys = params[@"property"];
    [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        // 检测这个对象是否存在该属性
        if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {
            // 利用kvc赋值
            [instance setValue:obj forKey:key];
        }
    }];

    NSString *terminate = [[NSUserDefaults standardUserDefaults] objectForKey:applicationWillTerminate];
    if (![terminate isEqualToString:applicationWillTerminate]) {
        // 获取导航控制器
        UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;
        UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];
        // 跳转到对应的控制器
        [pushClassStance pushViewController:instance animated:YES];
    }
}



- (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName
{
    unsigned int outCount, i;

    // 获取对象里的属性列表
    objc_property_t * properties = class_copyPropertyList([instance
                                                           class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property =properties[i];
        //  属性名转成字符串
        NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        // 判断该属性是否存在
        if ([propertyName isEqualToString:verifyPropertyName]) {
            free(properties);
            return YES;
        }
    }
    free(properties);

    return NO;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图