一.Java异常捕获:
1.1.Java异常概要:
所有RuntimeException类及其子类的实例被称为Runtime异常,即UnChecked Exception,不是RuntimeException类及其子类的异常实例则被称为Checked Exception。
(1)Checked异常又称为编译时异常,即在编译阶段被处理的异常。编译器会强制程序处理所有的Checked异常,也就是用try…catch显式的捕获并处理,因为Java认为这类异常都是可以被处理(修复)的。
(2)UnChecked异常又称为运行时异常,即Runtime-Exception,最常见的莫过于NullPointerException。UnChecked异常发生时,由于没有相应的try…catch处理该异常对象,所以Java运行环境将会终止,程序将退出,也就是我们所说的Crash。当然,你可能会说,那我们把这些异常也try…catch住不就行了。
所有的异常都是可以被捕获的。
1.2.Java处理未捕获异常:
Java提供了一个接口给我们,可以完成这些,这就是UncaughtExceptionHandler,该接口含有一个纯虚函数:public abstract void uncaughtException (Thread thread, Throwableex)。
Uncaught异常发生时会终止线程,此时,系统便会通知UncaughtExceptionHandler,告诉它被终止的线程以及对应的异常,然后便会调用uncaughtException函数。如果该handler没有被显式设置,则会调用对应线程组的默认handler。如果我们要捕获该异常,必须实现我们自己的handler,并通过以下函数进行设置:
public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler)
实现自定义的handler,只需要继承UncaughtExceptionHandler该接口,并实现uncaughtException方法即可。
static class MyCrashHandler implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread thread, final Throwable throwable) {
// Deal this exception
}
}
在任何线程中,都可以通过setDefaultUncaughtExceptionHandler来设置handler,但在Android应用程序中,全局的Application和Activity、Service都同属于UI主线程,线程名称默认为“main”。所以,在Application中应该为UI主线程添加UncaughtExceptionHandler,这样整个程序中的Activity、Service中出现的UncaughtException事件都可以被处理。
如果多次调用setDefaultUncaughtExceptionHandler设置handler,以最后一次为准。这也就是为什么多个抓崩溃的SDK同时使用,总会有一些SDK工作不正常。某些情况下,用户会既想利用第三方SDK收集崩溃,又想根据崩溃类型做出业务相关的处理。此时有两种方案:
SDK提供了一个接口,允许用户传递自己handler给SDK。当执行uncaughtException的时候,SDK只负责采集崩溃信息,之后便调用用户的handler来处理崩溃了。这儿其实类似于多了一层setDefaultUncaughtExceptionHandler,只是不能和标准库用同样的名称,因为会覆盖。
用户先通过setDefaultUncaughtExceptionHandler设置自己的handler,然后SDK再次通过setDefaultUncaughtExceptionHandler设自handler前,先保存之前的handler,在SDK handler逻辑处理完成之后,再调用之前的handler处理该异常。
static class MyCrashHandler implements UncaughtExceptionHandler {
private UncaughtExceptionHandler originalHandler;
private MyCrashHandler(Context context) {
originalHandler = Thread.getDefaultUncaughtExceptionHandler();
}
@Override
public void uncaughtException(Thread thread, final Throwable throwable) {
// Deal this exception
if (originalHandler != null) {
originalHandler.uncaughtException(thread, throwable);
}
}
}
获取Exception崩溃堆栈:
捕获Exception之后,我们还需要知道崩溃堆栈的信息,这样有助于我们分析崩溃的原因,查找代码的Bug。异常对象的printStackTrace方法用于打印异常的堆栈信息,根据printStackTrace方法的输出结果,我们可以找到异常的源头,并跟踪到异常一路触发的过程。
public static String getStackTraceInfo(final Throwable throwable) {
String trace = "";
try {
Writer writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
throwable.printStackTrace(pw);
trace = writer.toString();
pw.close();
} catch (Exception e) {
return "";
}
return trace;
}
二.Native异常捕获:
2.1.信号机制:
2.2.native异常常见信号量类型:
#define SIGHUP 1 // 终端连接结束时发出(不管正常或非正常)
#define SIGINT 2 // 程序终止(例如Ctrl-C)
#define SIGQUIT 3 // 程序退出(Ctrl-\)
#define SIGILL 4 // 执行了非法指令,或者试图执行数据段,堆栈溢出
#define SIGTRAP 5 // 断点时产生,由debugger使用
#define SIGABRT 6 // 调用abort函数生成的信号,表示程序异常
#define SIGIOT 6 // 同上,更全,IO异常也会发出
#define SIGBUS 7 // 非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数
#define SIGFPE 8 // 计算错误,比如除0、溢出
#define SIGKILL 9 // 强制结束程序,具有最高优先级,本信号不能被阻塞、处理和忽略
#define SIGUSR1 10 // 未使用,保留
#define SIGSEGV 11 // 非法内存操作,与 SIGBUS不同,他是对合法地址的非法访问, 比如访问没有读权限的内存,向没有写权限的地址写数据
#define SIGUSR2 12 // 未使用,保留
#define SIGPIPE 13 // 管道破裂,通常在进程间通信产生
#define SIGALRM 14 // 定时信号,
#define SIGTERM 15 // 结束程序,类似温和的 SIGKILL,可被阻塞和处理。通常程序如 果终止不了,才会尝试SIGKILL
#define SIGSTKFLT 16 // 协处理器堆栈错误
#define SIGCHLD 17 // 子进程结束时, 父进程会收到这个信号。
#define SIGCONT 18 // 让一个停止的进程继续执行
#define SIGSTOP 19 // 停止进程,本信号不能被阻塞,处理或忽略
#define SIGTSTP 20 // 停止进程,但该信号可以被处理和忽略
#define SIGTTIN 21 // 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号
#define SIGTTOU 22 // 类似于SIGTTIN, 但在写终端时收到
#define SIGURG 23 // 有紧急数据或out-of-band数据到达socket时产生
#define SIGXCPU 24 // 超过CPU时间资源限制时发出
#define SIGXFSZ 25 // 当进程企图扩大文件以至于超过文件大小资源限制
#define SIGVTALRM 26 // 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
#define SIGPROF 27 // 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间
#define SIGWINCH 28 // 窗口大小改变时发出
#define SIGIO 29 // 文件描述符准备就绪, 可以开始进行输入/输出操作
#define SIGPOLL SIGIO // 同上,别称
#define SIGPWR 30 // 电源异常
#define SIGSYS 31 // 非法的系统调用