RN捕获异常

目的

思路

  1. alert 是否统一在rn中写,还是在原生中写?

  2. 拼接字符串 (系统+版本号+项目+info)

  3. 在信分期的项目中用现金贷的接口

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()
  })
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值