一、造成系统崩溃(crash)的原因是什么?
(1)未捕获的异常
有Java 层面,和Native 层面 。 在我们平常操作中,如果有未捕获的异常,会导致系统崩溃,这个崩溃本质上是人为操作的,人为去操作系统主动退出(后面讲源码的地方可以看到)。
(2)ANR
系统因为线程阻塞问题,导致的无响应。
(3)WTF(What a Terrible Failure)
一般指,系统中自己编码没有按照android要求进行,例如发送未受保护的广播、启动的Activity未在注册文件里注册等等;这个一般不用太关注,主要写代码按标准来就行。
二、java层未捕获的异常是如何导致崩溃的
源码分析步骤1
首先我们先明确一个目的,我们的main函数的启用本身作为主线程存在,那么在我们想要探索系统到底如何处理异常,需要去关注到一个类Thread;
在我们的程序代码中,如果存在一个异常,任何地方都没有去捕获处理它的话,它就会一路往上抛,最终来到main函数,如果main函数也没有处理这个异常,就会给到JVM来处理,JVM会给到当前的线程Thread来处理。
源码分析步骤2
在Thread类中,看到一段这样的函数dispatchUncaughtException();
注释翻译为 :向处理程序发送未捕获的异常。此方法旨在仅由JVM调用; 可以理解为,未处理的异常 会走到这里来: 在上图我们可以看到官方明确告知,JVM在处理未经捕获的异常时 ,会调用当前dispatchUncaughtException函数进行处理,这个里面我们能看到一个类型为UncaughtExceptionHandler的类。 在上图的逻辑中我们可以看到如果没有设置 uncaughtExceptionHandler,将使用线程所在的线程组( ThreadGroup ) 来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。
源码分析步骤3
所以,我们重点来看ThreadGroup中,是如何来处理未捕获异常的:在Thread类的dispatchUncaughtException函数中,最后调用了getUncaughtExceptionHandler().uncaughtException(this, e); 我们知道这个getUncaughtExceptionHandler()返回的是ThreadGroup,所以我们来看ThreadGroup中的uncaughtException方法: 默认情况下,ThreadGroup处理未捕获异常的逻辑是:
-
首先将异常消息通知给父线程组( 如果parent不为空的话 ) ;
-
然后尝试利用一个默认的defaultUncaughtExceptionHandler来处理异常;
-
如果没有默认的异常处理器则将错误信息输出打印到System.err。
这里可以思考下,我们可以自定义 一个异常处理类 ,继承下 Thread.UncaughtExceptionHandler,然后去处理未捕获的异常。记得需要手动去调用Thread.setUncaughtExceptionPreHandler()方法设置下,有了这个自定义异常处理类,就可以做相应的崩溃优化。
源码分析步骤4
回到Thread中,思考下:既然他是通过getDefaultUncaughtExceptionHandler来处理,现在我们并没有看到有相关的设置,但是在Thread中我们看到了他对外提供了对应的设置函数:Thread.setUncaughtExceptionPreHandler()。
源码分析步骤5
思考下:系统是否会有地方默认给我们设置了uncaughtExceptionHandler?
因为从上面的源码看来,我们并没有看到有让系统直接崩溃掉的情况,因为默认是ThreadGroup去处理,他只不过是做了一个日志信息的记录 ,不会有退出的情况,那么肯定是有哪个地方默认设置了 uncaughtExceptionHandler,让系统退出的。
源码分析步骤6
来看下RuntimeInit这个类,zygote负责启动RuntimeInit进程(作用:app运行时环境初始化,用来初始化运行时的一系列信息,其中包含异常处理 ),它里面有个main方法: 这里设置了默认的异常处理:KillApplicationHandler。
源码分析步骤7
我们来看下KillApplicationHandler 重点来看 uncaughtException(Thread t, Throwable e)这个方法: 看到这里就知道了,默认的异常处理(杀进程 )是在RuntimeInit进程 (作用:app运行时环境初始化,用来初始化运行时的一系列信息,其中包含异常处理)的main()方法里设置的。
小总结
三、AMS如何承接应用的异常信息上报?
在上面的源码分析步骤7 我们知道了,在KillApplicationHandler的uncaughtException()方法里,最终异常信息有一个AMS上报过程: 来看下ActivityManagerService.handleApplicationCrash()方法: 从上面可以看出,若传入app为null时,processName就设置为system_server,意思是: 如果没有来源默认判定是系统进程自己。接着看handleApplicationCrashInner(String eventType......)方法:
参数eventType是指事件类型,具体如下:
- Java层未捕捉的异常:crash
- ANR:anr
- native层的异常:native_crash
现在我们看的是java的异常,所以这个类型传的是crash。
接着看handleApplicationCrashInner()函数:
中间的可以先不管他们,这个可以理解为在进行系统日志输出,具体的处理是在addErrorToDropBox()函数中。
重点注意 :无论是java crash、native_crash、ANR或是wtf,最终都是来到这里,交由addErrorToDropBox()函数去处理。
四、DropBoxManager
addErrorToDropBox()函数和DropBoxManager有关,Android Dropbox 是 Android 在 Froyo(API level 8) 引入的用来持续化存储系统数据的机制。主要用于记录 Android 运行过程中, 内核, 系统进程, 用户进程等出现严重问题时的 log, 可以认为这是一个可持续存储的系统级别的 logcat。
记录位置:在data/system/dropbox中:
也就是说,我们想要看系统的崩溃日志,可以在这个文件路径下找。