目的
思路
alert 是否统一在rn中写,还是在原生中写?
拼接字符串 (系统+版本号+项目+info)
在信分期的项目中用现金贷的接口
android
init
//
getName()
show()
exit()
//当UncaughtException发生时会转入该函数来处理
uncaughtException()
//自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
handleException()
//收集设备参数信息
collectDeviceInfo()
//保存错误信息到文件中
getCatchString()
//test
testException()
CrashHandler.java
package com.xfq.app;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.LifecycleEventListener;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class CrashHandler extends ReactContextBaseJavaModule implements UncaughtExceptionHandler {
private Promise jsPromise;
// 系统默认的UncaughtException处理类
private UncaughtExceptionHandler mDefaultHandler = null;
// 程序的Context对象
private Context mContext = null;
// 用来存储设备信息和异常信息
private Map<String, String> infos = new HashMap<String, String>();
/**
* 初始化
*/
public CrashHandler(ReactApplicationContext reactContext) {
super(reactContext);
mContext = reactContext;
// 获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// // 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public String getName() {
return "UncaughtExceptionHandler";
}
@ReactMethod
public void show(Promise promise) {
this.jsPromise = promise;
testException();
}
@ReactMethod
public void exit() {
System.exit(0);
// ActivityManager.AppExit(mContext);
}
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(final Throwable ex) {
if (ex == null) {
return false;
}
// 收集设备参数信息
// collectDeviceInfo(mContext);
this.jsPromise.resolve(getCatchString(ex));
return true;
}
/**
* 收集设备参数信息
*
* @param ctx
*/
public void collectDeviceInfo(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
infos.put("Build.VERSION.SDK_INT",Build.VERSION.SDK_INT+"");
}
} catch (Exception e) {
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
// LogUtil.d(TAG, field.getName() + " : " + field.get(null));
} catch (Exception e) {
}
}
}
/**
* 保存错误信息到文件中
*
* @param ex
* @return 返回文件名称,便于将文件传送到服务器
*/
private String getCatchString(Throwable ex) {
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
return sb.toString();
}
public void testException() {
new Thread(new Runnable()
{
public void run()
{
try {
Thread.currentThread().sleep(3000);//阻断3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
String msg = "";
System.out.println(msg.charAt(1));
}
}).start();
}
}
CrashHandlerPackage.java
package com.xfq.app;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Map;
import java.util.Arrays;
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
public class CrashHandlerPackage implements ReactPackage {
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new CrashHandler(reactContext));
return modules;
}
}
ios
.h
//
// UncaughtExceptionHandler.h
// XinfenqiApp
//
// Created by yyt on 2016/10/8.
// Copyright © 2016年 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "RCTBridgeModule.h"
@interface UncaughtExceptionHandler : NSObject <RCTBridgeModule>
{
BOOL dismissed;
}
@property RCTPromiseResolveBlock resolve;
@property RCTPromiseRejectBlock reject;
@end
void HandleException(NSException *exception);
void SignalHandler(int signal);
void InstallUncaughtExceptionHandler(void);
//
// UncaughtExceptionHandler.m
// XinfenqiApp
//
// Created by yyt on 2016/10/8.
// Copyright © 2016年 Facebook. All rights reserved.
//
#import "UncaughtExceptionHandler.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>
NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";
volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;
const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;
const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;
static UncaughtExceptionHandler *defaultHandler = nil;
@implementation UncaughtExceptionHandler
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(show:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
defaultHandler = self;
dispatch_sync(dispatch_get_main_queue(), ^(){
// AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//
self.resolve = resolve;
// self.reject = reject;
InstallUncaughtExceptionHandler();
// [self performSelector:@selector(ssss) withObject:nil afterDelay:5.0];
});
}
RCT_EXPORT_METHOD(exit){
exit(0);
}
+ (NSArray *)backtrace
{
void* callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
int i;
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
for (
i = UncaughtExceptionHandlerSkipAddressCount;
i < UncaughtExceptionHandlerSkipAddressCount +
UncaughtExceptionHandlerReportAddressCount;
i++)
{
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backtrace;
}
- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex
{
if (anIndex == 0)
{
dismissed = YES;
}else if (anIndex==1) {
NSLog(@"ssssssss");
}
}
- (void)validateAndSaveCriticalApplicationData
{
}
- (void)handleException:(NSException *)exception
{
[self validateAndSaveCriticalApplicationData];
self.resolve([exception reason]);
/*
UIAlertView *alert =
[[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"抱歉,程序出现了异常", nil)
message:[NSString stringWithFormat:NSLocalizedString(
@"如果点击继续,程序有可能会出现其他的问题,建议您还是点击退出按钮并重新打开\n\n"
@"异常原因如下:\n%@\n%@", nil),
[exception reason],
[[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]]
delegate:self
cancelButtonTitle:NSLocalizedString(@"退出", nil)
otherButtonTitles:NSLocalizedString(@"继续", nil), nil];
[alert show];
*/
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (!dismissed)
{
for (NSString *mode in (__bridge NSArray *)allModes)
{
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
}
CFRelease(allModes);
NSSetUncaughtExceptionHandler(NULL);
signal(SIGABRT, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName])
{
kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);
}
else
{
[exception raise];
}
}
@end
void HandleException(NSException *exception)
{
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
if (exceptionCount > UncaughtExceptionMaximum)
{
return;
}
NSArray *callStack = [UncaughtExceptionHandler backtrace];
NSMutableDictionary *userInfo =
[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
[userInfo
setObject:callStack
forKey:UncaughtExceptionHandlerAddressesKey];
[defaultHandler
performSelectorOnMainThread:@selector(handleException:)
withObject:
[NSException
exceptionWithName:[exception name]
reason:[exception reason]
userInfo:userInfo]
waitUntilDone:YES];
}
void SignalHandler(int signal)
{
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
if (exceptionCount > UncaughtExceptionMaximum)
{
return;
}
NSMutableDictionary *userInfo =
[NSMutableDictionary
dictionaryWithObject:[NSNumber numberWithInt:signal]
forKey:UncaughtExceptionHandlerSignalKey];
NSArray *callStack = [UncaughtExceptionHandler backtrace];
[userInfo
setObject:callStack
forKey:UncaughtExceptionHandlerAddressesKey];
[defaultHandler
performSelectorOnMainThread:@selector(handleException:)
withObject:
[NSException
exceptionWithName:UncaughtExceptionHandlerSignalExceptionName
reason:
[NSString stringWithFormat:
NSLocalizedString(@"Signal %d was raised.", nil),
signal]
userInfo:
[NSDictionary
dictionaryWithObject:[NSNumber numberWithInt:signal]
forKey:UncaughtExceptionHandlerSignalKey]]
waitUntilDone:YES];
}
void InstallUncaughtExceptionHandler(void)
{
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
}
RN
如果是RN的代码错误,在alert之前,程序以及崩溃,alert无法调用显示。
异常之后,就再也调用不了原生模块了
const ErrorUtils = require('ErrorUtils');
getCrash(){
var str = moment().format("YYYY-MM-DD HH:mm")+'|' + DeviceInfo.getBrand()+ DeviceInfo.getModel()+'|' + DeviceInfo.getSystemVersion()+'|' + DeviceInfo.getBundleId()+'|' + DeviceInfo.getVersion()+'|'
if (!__DEV__) {
ErrorUtils.setGlobalHandler(error => {
console.log("很抱歉error---",str + error);
this.showAlert()
})
}
NativeModules.UncaughtExceptionHandler.show()
.then(data => {
console.log('获取异常info',str + data)
this.showAlert()
})
}