由于项目最近crash率比较高,为了更好地监控线上程序包的crash,所以开发将异常崩溃捕获发送钉钉消息的功能。
异常的捕获
通过使用NSSetUncaughtExceptionHandler方法将异常捕获然后进行处理。
在Appdelegate中定义如下函数,用来处理捕获到的异常。
1 2 3 4 5 6 7 8 9 10 11
| void uncaughtExceptionHandler(NSException *exception)
{ // 异常的堆栈信息 NSArray *stackArray = [exception callStackSymbols]; // 出现异常的原因 NSString *reason = [exception reason]; // 异常名称 NSString *name = [exception name]; //TODO 发送钉钉消息 }
|
下面是捕获异常的代码,didFinishLaunchingWithOptions方法中写入:
1
| NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
|
到这里就可以进行异常捕获了,下面我们写一段让程序崩溃的代码进行测试
1 2 3
| - (void)crashMethod{ [self performSelector:@selector(thisMthodDoesNotExist) withObject:nil];//调用一个不存在的方法。 }
|
到这里uncaughtExceptionHandler就可以捕获到应用的崩溃Exception了。
第三方冲突
如果项目里使用了友盟等第三方异常监控分析工具,会和我们NSSetUncaughtExceptionHandler方法冲突,导致第三方不会上传异常信息,解决方案是使用NSGetUncaughtExceptionHandler()获取到第三方捕获异常的Handler,当我们处理完异常将NSException对象传给第三方的Handler。
1
| static NSUncaughtExceptionHandler *_previousHandler;//Appdelegate定义一个全局静态变量用来保存第三方的异常Handler
|
didFinishLaunchingWithOptions方法中在NSSetUncaughtExceptionHandler之前先保存之前的Handler:
1 2 3
| // 保存第三方处理异常的handler _previousHandler = NSGetUncaughtExceptionHandler(); NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
|
最后在uncaughtExceptionHandler函数最后(即我们自定义的异常处理之后),将exception回传给第三方Handler。
1 2 3 4
| //将exception回传给第三方Handler if (_previousHandler) { _previousHandler(exception); }
|
给钉钉群发送消息
#####1、首先是在钉钉群里新建一个自定义机器人,获取到机器人的webhook。详细的步骤可以看钉钉API文档:
https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.Uf9uaN&treeId=257&articleId=105735&docType=1#s1
#####2、向机器人的webhook发送post消息。
下面是用AFNetworking发送post请求的代码,注意请求参数的格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| NSDictionary *dict = @{ @"msgtype":@"text", @"text":@{@"content":self.errorMessage?self.errorMessage:@""}, @"at":@{ @"atMobiles":self.arrayAtPhones?self.arrayAtPhones:@[], @"isAtAll":@NO } }; NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error]; AFURLSessionManager *sessionManager = [[AFURLSessionManager alloc] init]; AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializer]; responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html", @"text/plain", @"image/jpeg", nil]; sessionManager.responseSerializer = responseSerializer; NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.dingdingUploadUrl]]; [req setHTTPMethod:@"POST"]; [req setHTTPBody:jsonData]; //不设置会返回参数错误 [req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [[sessionManager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { }]resume];
|
到这里就可以正常给钉钉发送消息了。纯手敲难免有手误,哪里不准确的地方请指出。
demo工程地址:https://github.com/CoderJasons/-CatchCrashTest