android崩溃系列-崩溃原理分析
文章目录
- 结论
- 源码分析
- 使用
结论
在java默认的异常处理机制中,是没有崩溃退出这个说法的,而在android中的RuntimeInit对其拦截并且处理。
源码分析
- 首先关注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);}
我们来跟踪一下getUncaughtExceptionHandler()这个方法,如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。
//java/lang/Thread.java //源码备注翻译:返回当此线程由于未捕获的异常而突然终止时调用的处理程序。如果该线程没有显式设置未捕获的异常处理程序,则返回该线程的ThreadGroup对象,除非该线程已终止,在这种情况下返回null 。 public UncaughtExceptionHandler getUncaughtExceptionHandler() {return uncaughtExceptionHandler != null ?uncaughtExceptionHandler : group;}
默认情况下,线程组处理未捕获异常的逻辑时,首先将异常消息通知给父线程组然后尝试利用一个默认的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);}}}
思考下,uncaughtExceptionHandler在为空的情况下再回采用默认的方式ThreadGroup处理,在ThreadGroup处理时,它首先通过getDefaultUncaughtExceptionHandler来处理,但是在Thread中我们看到了他对外提供了对应的设置函数如下setDefaultUncaughtExceptionHandler()
并且我们知道,程序在出现错误后会有退出程序的操作,但是目前我们并没有看见退出程序的操作,我们继续跟踪分析。
//java/lang/Thread.java /** 设置当线程由于未捕获的异常而突然终止时调用的默认处理程序,并且没有为该线程定义其他处理程序。**/ public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {defaultUncaughtExceptionHandler = eh;}
来我们用AndroidStudio看看setDefaultUncaughtExceptionHandler调用,我们发现了RuntimtInit类以及KillApplicationHandler类(是不是很激动)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cc9JzaVJ-1662304608259)(default调用)]
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!");}
我们继续分析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); ....优雅的省略号....}
此时我们看到了KillApplicationHandler,此类是android内部默认初始化设置的主线程异常处理方案,由此我们可以看见Android中怎样处理异常的。
看uncaughtException的逻辑我们可以很明确的看到
- 他直接提取的是当前ActivityThread的当前线程,然后直接stopProFiling;
- 上报AMS崩溃异常信息;
- 在最终的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);}}
到此我们可以明确的知道导致崩溃的原因,到此我们先阶段总结一下:
- 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崩溃系列-崩溃原理分析相关推荐
- 蓝牙App系列漏洞原理分析与漏洞利用
蓝牙App系列漏洞原理分析与漏洞利用 作者: heeeeen 本文系转载,目的是学习,如有侵权,请联系删除 转载出处:http://www.ms509.com/ 蓝牙App漏洞系列分析之一CVE-20 ...
- Android手机一键Root原理分析(作者:非虫,文章来自:《黑客防线》2012年7月)
之前几天都在做Android漏洞分析的项目,万幸发现了这篇文章.废话不多说,上文章! <Android手机一键Root原理分析> (作者:非虫,文章来自:<黑客防线>2012年 ...
- Android系统的JNI原理分析(二)- 数据类型转换和方法签名
声明 前阶段在项目中使用了Android的JNI技术,在此文中做些技术知识总结. 本文参考了一些书籍的若干章节,比如<Android进阶解密-第9章-JNI原理>.<深入理解Andr ...
- 事件争夺战 Android事件处理机制与原理分析
事件争夺战 Android事件处理机制与原理分析 文章目录 事件争夺战 Android事件处理机制与原理分析 View的继承关系 View的事件处理源码 总结: ViewGroup的事件分发源码 总结 ...
- Android锁屏机制原理分析
转载自:http://www.2cto.com/kf/201401/273898.html 春节前最后几天了,工作上几乎没有什么要做.大致整理下之前工作中写的文档,PPT,手册. 由于去年一年完全转到 ...
- Android TV系列 TV APP分析(二)
在ATV SDK中,TV APP也是一个比较重要的apk,他负责显示各种输入源,比如HDMI IN输入,AV IN输入,因为要显示不同的源,具体硬件平台又步一样,所以一定会涉及到相关定义.每一个不同的 ...
- Android 6.0 JNI原理分析 和 Linux系统调用(syscall)原理
JNI原理 引言:分析Android源码6.0的过程,一定离不开Java与C/C++代码直接的来回跳转,那么就很有必要掌握JNI,这是链接Java层和Native层的桥梁,本文涉及相关源码: fram ...
- Android面试题--HashMap原理分析
目录 一.序言 二 .HashMap原理分析 二.HashMap和Hashtable区别? 一.序言 作为Android程序员,出去找工作面试,HashMap应该是最常被问到的一种数据类型.那它是怎么 ...
- Android热修复技术原理分析
2015年以来,Android开发领域里对热修复技术的讨论和分享越来越多,同时也出现了一些不同的解决方案,如QQ空间补丁方案.阿里AndFix以 及微信Tinker,它们在原理各有不同,适用场景各异, ...
最新文章
- phpstorm常用设置
- JSP中文乱码分析和解决
- 使用shell根据端口号关闭进程
- Emlog百度快速收录插件
- HTML5 文本元素
- web开发的发展历史
- mysql主从北_mysql主从复制(超简单)
- ROS中测试机器人里程计信息
- Atitit 理财之道---支出大骗局分析与防范
- Python爬虫教程-22-lxml-etree和xpath配合使用
- 项目管理工具---用Excel制作甘特图(转)
- python中func函数_Python 函数(func)学习
- 建站之星网站 和服务器,建站之星网站 和服务器
- Aria2Gee 教程
- web开发路径问题解决
- 成功解决hostname ‘xxx‘ doesn‘t uniquely match the interface ‘ens33‘ selected for the management bridge
- dns 劫持是什么意思,DNS劫持有啥解决办法?
- oracle 存储打印输出,word2016打印输出 oracle 打印输出
- 股东转让股权的条件是什么
- maven_使用Maven Failsafe和JUnit @Category将集成测试与单元测试分开
热门文章
- Java微服务学习笔记(一):微服务架构的概念理解
- Python批量处理文件、图片、视频【干货建议收藏】
- java 对接 PayPal 或者 Stripe 支付,订阅
- c#设计12星座速配软件_星座代码:用vb编一个星座配对的程序代码
- OMS标准 第二卷 主要通讯 4.5.1 2022-12(中文手翻版本)
- 资金申报项目管理系统 - 系统介绍篇
- 【404 App】v2.0版本各个渠道统计
- 中国高速公路企业破局之道 ——三管齐下:流量变现、新觅赛道、精益运营
- 曾国藩《挺经》卷十五忠疑
- 什么是计算机主板芯片组,什么是Intel 4系列芯片组ICH7主板,支持哪些CPU?