文章目录

  • 结论
  • 源码分析
  • 使用

结论

在java默认的异常处理机制中,是没有崩溃退出这个说法的,而在android中的RuntimeInit对其拦截并且处理。

源码分析

  1. 首先关注Thread类中的dispatchUncaughtException,JVM在处理未经捕获的异常时,会调用当前dispatchUncaughtException函数进行处理,这个里面我们能看到一个类型为UncaughtExceptionHandler的类。
//java/lang/Thread.java
//源码备注翻译:将未捕获的异常分派给处理程序。此方法旨在仅由运行时和测试调用。
public final void dispatchUncaughtException(Throwable e) {Thread.UncaughtExceptionHandler initialUeh =Thread.getUncaughtExceptionPreHandler();if (initialUeh != null) {try {initialUeh.uncaughtException(this, e);} catch (RuntimeException | Error ignored) {// Throwables thrown by the initial handler are ignored}}//手动注意getUncaughtExceptionHandler().uncaughtException(this, e);}
  1. 我们来跟踪一下getUncaughtExceptionHandler()这个方法,如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。

    //java/lang/Thread.java
    //源码备注翻译:返回当此线程由于未捕获的异常而突然终止时调用的处理程序。如果该线程没有显式设置未捕获的异常处理程序,则返回该线程的ThreadGroup对象,除非该线程已终止,在这种情况下返回null 。
    public UncaughtExceptionHandler getUncaughtExceptionHandler() {return uncaughtExceptionHandler != null ?uncaughtExceptionHandler : group;}
    
  2. 默认情况下,线程组处理未捕获异常的逻辑时,首先将异常消息通知给父线程组然后尝试利用一个默认的defaultUncaughtExceptionHandler来处理异常如果没有默认的异常处理器则将错误信息输出到System.err(但是android在初始化时在RuntimeInit对uncaughtExceptionHandler进行的初始化),也同时可以知道此时应用并没有直接退出。

    //java/lang/ThreadGroup.java
    //官方注释翻译:
    /**当此线程组中的线程由于未捕获的异常而停止并且该线程没有设置特定的UncaughtExceptionHandler时,由 Java 虚拟机调用。
    此方法做了以下几个操作:
    1. 如果此线程组有父线程组,则使用相同的两个参数调用该父线程的 uncaughtException 方法。
    2. 否则,此方法会检查是否设置了 默认未捕获异常处理程序,如果是,则使用相同的方法调用其 uncaughtException 方法
    3. 否则,此方法确定 <code>Throwable<code> 参数是否是 {@link ThreadDeath} 的实例。如果是这样,没有什么特别的。否则,一条包含线程名称的消息(从线程的 {@link ThreadgetName getName} 方法返回)和使用 <code>Throwable<code> 的 {@link ThrowableprintStackTrace printStackTrace} 方法的堆栈回溯打印到{@linkplain Systemerr 标准错误流}。**/
    public void uncaughtException(Thread t, Throwable e) {if (parent != null) {parent.uncaughtException(t, e);} else {Thread.UncaughtExceptionHandler ueh =Thread.getDefaultUncaughtExceptionHandler();if (ueh != null) {ueh.uncaughtException(t, e);} else if (!(e instanceof ThreadDeath)) {System.err.print("Exception in thread \""+ t.getName() + "\" ");e.printStackTrace(System.err);}}}
    
  3. 思考下,uncaughtExceptionHandler在为空的情况下再回采用默认的方式ThreadGroup处理,在ThreadGroup处理时,它首先通过getDefaultUncaughtExceptionHandler来处理,但是在Thread中我们看到了他对外提供了对应的设置函数如下setDefaultUncaughtExceptionHandler()

    并且我们知道,程序在出现错误后会有退出程序的操作,但是目前我们并没有看见退出程序的操作,我们继续跟踪分析。

    //java/lang/Thread.java
    /**
    设置当线程由于未捕获的异常而突然终止时调用的默认处理程序,并且没有为该线程定义其他处理程序。**/
    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {defaultUncaughtExceptionHandler = eh;}
    
  4. 来我们用AndroidStudio看看setDefaultUncaughtExceptionHandler调用,我们发现了RuntimtInit类以及KillApplicationHandler类(是不是很激动)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cc9JzaVJ-1662304608259)(default调用)]

  5. RunTimeInit是由Zygote调用的,其初始化会初始化异常相关的操作。

    //com/android/internal/os/RuntimeInit.java
    public static final void main(String[] argv) {enableDdms();if (argv.length == 2 && argv[1].equals("application")) {if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");redirectLogStreams();} else {if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");}//注意此方法commonInit();/** Now that we're running in interpreted code, call back into native code* to run the system.*/nativeFinishInit();if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");}
  6. 我们继续分析commitInit方法,这里注意,我们能直观的在此处看到当前线程中设置了setDefaultUncaughtExceptionHandler—>其类型为KillApplicationHandler

      protected static final void commonInit() {if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");/** set handlers; these apply to all threads in the VM. Apps can replace* the default handler, but not the pre handler.*/LoggingHandler loggingHandler = new LoggingHandler();Thread.setUncaughtExceptionPreHandler(loggingHandler);//此处是关键Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));/** Install a TimezoneGetter subclass for ZoneInfo.db*/TimezoneGetter.setInstance(new TimezoneGetter() {@Overridepublic String getId() {return SystemProperties.get("persist.sys.timezone");}});TimeZone.setDefault(null);
    ....优雅的省略号....}
    
  7. 此时我们看到了KillApplicationHandler,此类是android内部默认初始化设置的主线程异常处理方案,由此我们可以看见Android中怎样处理异常的。

    看uncaughtException的逻辑我们可以很明确的看到

    1. 他直接提取的是当前ActivityThread的当前线程,然后直接stopProFiling;
    2. 上报AMS崩溃异常信息;
    3. 在最终的finally中直接kill掉进程,且退出当前进程
    //com/android/internal/os/RuntimeInit.java
    @Overridepublic void uncaughtException(Thread t, Throwable e) {try {ensureLogging(t, e);// Don't re-enter -- avoid infinite loops if crash-reporting crashes.if (mCrashing) return;mCrashing = true;// Try to end profiling. If a profiler is running at this point, and we kill the// process (below), the in-memory buffer will be lost. So try to stop, which will// flush the buffer. (This makes method trace profiling useful to debug crashes.)if (ActivityThread.currentActivityThread() != null) {ActivityThread.currentActivityThread().stopProfiling();}// Bring up crash dialog, wait for it to be dismissedActivityManager.getService().handleApplicationCrash(mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));} catch (Throwable t2) {if (t2 instanceof DeadObjectException) {// System process is dead; ignore} else {try {Clog_e(TAG, "Error reporting crash", t2);} catch (Throwable t3) {// Even Clog_e() fails!  Oh well.}}} finally {// Try everything to make sure this process goes away.Process.killProcess(Process.myPid());System.exit(10);}}
    
  8. 到此我们可以明确的知道导致崩溃的原因,到此我们先阶段总结一下:

    • JVM通过调用dispatchUncaughtException来进行未捕获异常处理
    • 具体对应的提供处理能力的是UncaughtExceptionHandler这个类
    • 默认ThreadGroup提供日志打印处理
    • 但是在进程环境初始化时RuntimeInit提供杀死进程的能力
    • 注意:既然他已经对外提供了设置UncaughtExceptionHandler的能力,那么我们自己可以编写Handler处理类来进行未捕获异常处理

使用

收集log便于分析:

在Application中设置Handler

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread thread, Throwable ex) {//这里处理记录上传log,并记得kill进程并退出。}});

优雅的处理:由于Thread中的defaultUncaughtExceptionHandler是静态的,当我们设置setDefaultUncaughtExceptionHandler()方法时,会把之前的设置的Handler替换掉,造成别人设置的失效,所以优雅的处理方式先调用getDefaultUncaughtExceptionHandler()方法将对象缓存起来后,再讲我们的Handler设置进去,当收到异常消息后,再通过保存的Handler消息将消息分发出去。

android崩溃系列-崩溃原理分析相关推荐

  1. 蓝牙App系列漏洞原理分析与漏洞利用

    蓝牙App系列漏洞原理分析与漏洞利用 作者: heeeeen 本文系转载,目的是学习,如有侵权,请联系删除 转载出处:http://www.ms509.com/ 蓝牙App漏洞系列分析之一CVE-20 ...

  2. Android手机一键Root原理分析(作者:非虫,文章来自:《黑客防线》2012年7月)

    之前几天都在做Android漏洞分析的项目,万幸发现了这篇文章.废话不多说,上文章! <Android手机一键Root原理分析> (作者:非虫,文章来自:<黑客防线>2012年 ...

  3. Android系统的JNI原理分析(二)- 数据类型转换和方法签名

    声明 前阶段在项目中使用了Android的JNI技术,在此文中做些技术知识总结. 本文参考了一些书籍的若干章节,比如<Android进阶解密-第9章-JNI原理>.<深入理解Andr ...

  4. 事件争夺战 Android事件处理机制与原理分析

    事件争夺战 Android事件处理机制与原理分析 文章目录 事件争夺战 Android事件处理机制与原理分析 View的继承关系 View的事件处理源码 总结: ViewGroup的事件分发源码 总结 ...

  5. Android锁屏机制原理分析

    转载自:http://www.2cto.com/kf/201401/273898.html 春节前最后几天了,工作上几乎没有什么要做.大致整理下之前工作中写的文档,PPT,手册. 由于去年一年完全转到 ...

  6. Android TV系列 TV APP分析(二)

    在ATV SDK中,TV APP也是一个比较重要的apk,他负责显示各种输入源,比如HDMI IN输入,AV IN输入,因为要显示不同的源,具体硬件平台又步一样,所以一定会涉及到相关定义.每一个不同的 ...

  7. Android 6.0 JNI原理分析 和 Linux系统调用(syscall)原理

    JNI原理 引言:分析Android源码6.0的过程,一定离不开Java与C/C++代码直接的来回跳转,那么就很有必要掌握JNI,这是链接Java层和Native层的桥梁,本文涉及相关源码: fram ...

  8. Android面试题--HashMap原理分析

    目录 一.序言 二 .HashMap原理分析 二.HashMap和Hashtable区别? 一.序言 作为Android程序员,出去找工作面试,HashMap应该是最常被问到的一种数据类型.那它是怎么 ...

  9. Android热修复技术原理分析

    2015年以来,Android开发领域里对热修复技术的讨论和分享越来越多,同时也出现了一些不同的解决方案,如QQ空间补丁方案.阿里AndFix以 及微信Tinker,它们在原理各有不同,适用场景各异, ...

最新文章

  1. 语言的神经结构: 一体化建模集中于预测处理(附ppt)
  2. 201521123029《Java程序设计》第1周学习总结
  3. opencv 将图片合成为视频流(AVI格式)
  4. 细节决定成败—关于.net的.dll.refresh文件
  5. java jolt tuxedo_java通过jolt调用tuxedo服务.xls
  6. 【KPGNN】运行错误与成功后的合集
  7. linux安装pgsql源码包解压,在Linux(centos)中使用源码安装pgRouting
  8. django 部署_狂野的Django:部署生存的技巧
  9. Pytorch —— 学习率调整策略
  10. 华为天才少年一人打造自动驾驶!
  11. 小白怎么入门自由职业?
  12. 递归--练习11--noi9273 PKU2506Tiling
  13. 伪原创写作-开启网站内容“山寨模式”
  14. C/C++ Bug记录
  15. 写得好 git 提交信息
  16. IDENT_CURRENT ,@@identity,SCOPE_IDENTITY() 之间对比
  17. SPSS统计检验中的边缘显著及其转化处理【SPSS 063期】
  18. 12个C语言必背实例
  19. Merged region B8 must contain 2 or more cells
  20. pdf2html java_pdf2HtmlEX的使用

热门文章

  1. 编程题刷题笔记(包括leetcode和各种公司笔试题)
  2. Android 实例-个人理财工具 之五 账单明细显示A
  3. java 时间段重叠_java 判断两个时间段是否重叠的案例
  4. 使您的软件运行起来: 防止缓冲区溢出(转)
  5. dx200手环使用方法_dx200电子烟安装说明书
  6. 中国高速公路行业投资规划创新及未来发展战略分析报告2021-2027年
  7. 分数化成有限小数的方法_分数化成小数最简单的方法就是直接用分子除以分母...
  8. pod install 报错 Insecure world writable, mode 040777
  9. C++ inline weak symbol and so on
  10. UnicodeConverteUtil: Java unicode 与 中文相互转换