目录总结

  • 00.异常处理几个常用api
  • 01.UncaughtExceptionHandler
  • 02.Java线程处理异常分析
  • 03.Android中线程处理异常分析
  • 04.为何使用setDefaultUncaughtExceptionHandler

前沿

00.异常处理几个常用api

  • setUncaughtExceptionHandler

    • public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

      • 设置该线程由于未捕获到异常而突然终止时调用的处理程序。
      • 通过明确设置未捕获到的异常处理程序,线程可以完全控制它对未捕获到的异常作出响应的方式。
      • 如果没有设置这样的处理程序,则该线程的 ThreadGroup 对象将充当其处理程序。
    • public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
      • 返回该线程由于未捕获到异常而突然终止时调用的处理程序。
      • 如果该线程尚未明确设置未捕获到的异常处理程序,则返回该线程的 ThreadGroup 对象,除非该线程已经终止,在这种情况下,将返回 null。
  • setDefaultUncaughtExceptionHandler
    • public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

      • 设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
      • 未捕获到的异常处理首先由线程控制,然后由线程的 ThreadGroup 对象控制,最后由未捕获到的默认异常处理程序控制。
      • 如果线程不设置明确的未捕获到的异常处理程序,并且该线程的线程组(包括父线程组)未特别指定其 uncaughtException 方法,则将调用默认处理程序的 uncaughtException 方法。 -- 通过设置未捕获到的默认异常处理程序,应用程序可以为那些已经接受系统提供的任何“默认”行为的线程改变未捕获到的异常处理方式(如记录到某一特定设备或文件)。
    • public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
      • 返回线程由于未捕获到异常而突然终止时调用的默认处理程序。
      • 如果返回值为 null,则没有默认处理程序。
  • Thread.UncaughtExceptionHandler
    • public static interface Thread.UncaughtExceptionHandler

      • 所有已知实现类:ThreadGroup
      • 当 Thread 因未捕获的异常而突然终止时,调用处理程序的接口。
      • 当某一线程因未捕获的异常而即将终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询该线程以获得其 UncaughtExceptionHandler 的线程,并调用处理程序的 uncaughtException 方法,将线程和异常作为参数传递。
      • 如果某一线程没有明确设置其 UncaughtExceptionHandler,则将它的 ThreadGroup 对象作为其 UncaughtExceptionHandler。
      • 如果 ThreadGroup 对象对处理异常没有什么特殊要求,那么它可以将调用转发给默认的未捕获异常处理程序。

01.UncaughtExceptionHandler

  • 官方介绍为:

    • Interface for handlers invoked when a Thread abruptly terminates due to an uncaught exception.
    • When a thread is about to terminate due to an uncaught exception the Java Virtual Machine will query the thread for its UncaughtExceptionHandler using getUncaughtExceptionHandler() and will invoke the handler's uncaughtException method, passing the thread and the exception as arguments. If a thread has not had its UncaughtExceptionHandler explicitly set, then its ThreadGroup object acts as its UncaughtExceptionHandler. If the ThreadGroup object has no special requirements for dealing with the exception, it can forward the invocation to the default uncaught exception handler.
  • 翻译后大概的意思是
    • UncaughtExceptionHandler接口用于处理因为一个未捕获的异常而导致一个线程突然终止问题。
    • 当一个线程因为一个未捕获的异常即将终止时,Java虚拟机将通过调用getUncaughtExceptionHandler() 函数去查询该线程的UncaughtExceptionHandler并调用处理器的 uncaughtException方法将线程及异常信息通过参数的形式传递进去。如果一个线程没有明确设置一个UncaughtExceptionHandler,那么ThreadGroup对象将会代替UncaughtExceptionHandler完成该行为。如果ThreadGroup没有明确指定处理该异常,ThreadGroup将转发给默认的处理未捕获的异常的处理器。
  • 异常回调:uncaughtException
    • uncaughtException (Thread t, Throwable e) 是一个抽象方法,当给定的线程因为发生了未捕获的异常而导致终止时将通过该方法将线程对象和异常对象传递进来。
  • 设置默认未捕获异常处理器:setDefaultUncaughtExceptionHandler
    • void setDefaultUncaughtExceptionHandler (Thread.UncaughtExceptionHandler eh)
    • 设置一个处理者当一个线程突然因为一个未捕获的异常而终止时将自动被调用。
    • 未捕获的异常处理的控制第一个被当前线程处理,如果该线程没有捕获并处理该异常,其将被线程的ThreadGroup对象处理,最后被默认的未捕获异常处理器处理。
    • 通过设置默认的未捕获异常的处理器,对于那些早已被系统提供了默认的未捕获异常处理器的线程,一个应用可以改变处理未捕获的异常的方式,例如记录到指定的设备或者文件。
  • handler将会报告线程终止和不明原因异常这个情况,如果没有自定义handler, 线程管理组就被默认为报告异常的handler。
    • ThreadHandler 这个类就是实现了UncaughtExceptionHandler这个接口,伪代码代码如下所示
    public class ThreadHandler implements Thread.UncaughtExceptionHandler {
    
        private Thread.UncaughtExceptionHandler mDefaultHandler;
    private boolean isInit = false;
    /**
    * CrashHandler实例
    */
    private static ThreadHandler INSTANCE; /**
    * 获取CrashHandler实例 ,单例模式
    */
    public static ThreadHandler getInstance() {
    if (INSTANCE == null) {
    synchronized (CrashHandler.class) {
    if (INSTANCE == null) {
    INSTANCE = new ThreadHandler();
    }
    }
    }
    return INSTANCE;
    } /**
    * 当UncaughtException发生时会转入该函数来处理
    * 该方法来实现对运行时线程进行异常处理
    */
    @Override
    public void uncaughtException(Thread t, Throwable e) {
    if (mDefaultHandler != null) {
    //收集完信息后,交给系统自己处理崩溃
    //uncaughtException (Thread t, Throwable e) 是一个抽象方法
    //当给定的线程因为发生了未捕获的异常而导致终止时将通过该方法将线程对象和异常对象传递进来。
    mDefaultHandler.uncaughtException(t, e);
    } else {
    //否则自己处理
    }
    } /**
    * 初始化,注册Context对象,
    * 获取系统默认的UncaughtException处理器,
    * 设置该CrashHandler为程序的默认处理器
    * @param ctx
    */
    public void init(Application ctx) {
    if (isInit){
    return;
    }
    //获取系统默认的UncaughtExceptionHandler
    mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
    //将当前实例设为系统默认的异常处理器
    //设置一个处理者当一个线程突然因为一个未捕获的异常而终止时将自动被调用。
    //未捕获的异常处理的控制第一个被当前线程处理,如果该线程没有捕获并处理该异常,其将被线程的ThreadGroup对象处理,最后被默认的未捕获异常处理器处理。
    Thread.setDefaultUncaughtExceptionHandler(this);
    isInit = true;
    }
    }

02.Java线程处理异常分析

  • 线程出现未捕获异常后,JVM将调用Thread中的dispatchUncaughtException方法把异常传递给线程的未捕获异常处理器。

    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);
    } public static UncaughtExceptionHandler getUncaughtExceptionPreHandler() {
    return uncaughtExceptionPreHandler;
    }
  • Thread中存在两个UncaughtExceptionHandler。一个是静态的defaultUncaughtExceptionHandler,另一个是非静态uncaughtExceptionHandler。
    // null unless explicitly set
    private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null unless explicitly set
    private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
    • defaultUncaughtExceptionHandler:设置一个静态的默认的UncaughtExceptionHandler。来自所有线程中的Exception在抛出并且未捕获的情况下,都会从此路过。进程fork的时候设置的就是这个静态的defaultUncaughtExceptionHandler,管辖范围为整个进程。
    • uncaughtExceptionHandler:为单个线程设置一个属于线程自己的uncaughtExceptionHandler,辖范围比较小。
  • 没有设置uncaughtExceptionHandler怎么办?
    • 如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。
    • 线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。ThreadGroup类定义:
    private ThreadGroup group;
    //可以发现ThreadGroup类是集成Thread.UncaughtExceptionHandler接口的
    class ThreadGroup implements Thread.UncaughtExceptionHandler{}
  • 然后看一下ThreadGroup中实现uncaughtException(Thread t, Throwable e)方法,代码如下
    • 默认情况下,线程组处理未捕获异常的逻辑是,首先将异常消息通知给父线程组,
    • 然后尝试利用一个默认的defaultUncaughtExceptionHandler来处理异常,
    • 如果没有默认的异常处理器则将错误信息输出到System.err。
    • 也就是JVM提供给我们设置每个线程的具体的未捕获异常处理器,也提供了设置默认异常处理器的方法。
    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);
    }
    }
    }

03.Android中线程处理异常分析

  • 在Android平台中,应用进程fork出来后会为虚拟机设置一个未截获异常处理器, 即在程序运行时,如果有任何一个线程抛出了未被截获的异常, 那么该异常最终会抛给未截获异常处理器处理。

    • 具体可以找到RuntimeInit类,然后在找到KillApplicationHandler类。首先看该类的入口main方法--->commonInit()--->,然后接着往下走,找到setDefaultUncaughtExceptionHandler代码如下所示
    • 如果报告崩溃,不要再次进入——避免无限循环。如果ActivityThread分析器在此时运行,我们杀死进程,内存中的缓冲区将丢失。并且打开崩溃对话框
    • 最后会执行finally中杀死进程的方法干掉app
    Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
    
    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
    private final LoggingHandler mLoggingHandler;
    @Override
    public void uncaughtException(Thread t, Throwable e) {
    try {
    if (mCrashing) return;
    mCrashing = true;
    if (ActivityThread.currentActivityThread() != null) {
    ActivityThread.currentActivityThread().stopProfiling();
    }
    // Bring up crash dialog, wait for it to be dismissed
    ActivityManager.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);
    }
    }
    }
  • UncaughtExceptionHandler存在于Thread中.当异常发生且未捕获时。异常会透过UncaughtExceptionHandler抛出。并且该线程会消亡。所以在Android中子线程死亡是允许的。主线程死亡就会导致ANR。
  • 所以其实在fork出app进程的时候,系统已经为app设置了一个异常处理,并且最终崩溃后会直接导致执行该handler的finallly方法最后杀死app直接退出app。如果你要自己处理,你可以自己实现Thread.UncaughtExceptionHandler。

04.为何使用setDefaultUncaughtExceptionHandler

  • Thread.UncaughtExceptionHandler 接口代码如下所示

    @FunctionalInterface
    public interface UncaughtExceptionHandler {
    void uncaughtException(Thread t, Throwable e);
    }
    • UncaughtExceptionHandler 未捕获异常处理接口,当一个线程由于一个未捕获异常即将崩溃时,JVM 将会通过 getUncaughtExceptionHandler() 方法获取该线程的 UncaughtExceptionHandler,并将该线程和异常作为参数传给 uncaughtException()方法。
    • 如果没有显式设置线程的 UncaughtExceptionHandler,那么会将其 ThreadGroup 对象会作为 UncaughtExceptionHandler。
    • 如果其 ThreadGroup 对象没有特殊的处理异常的需求,那么就会调 getDefaultUncaughtExceptionHandler() 方法获取默认的 UncaughtExceptionHandler 来处理异常。
  • 难道要为每一个线程创建UncaughtExceptionHandler吗?
    • 应用程序通常都会创建很多线程,如果为每一个线程都设置一次 UncaughtExceptionHandler 未免太过麻烦。
    • 既然出现未处理异常后 JVM 最终都会调 getDefaultUncaughtExceptionHandler(),那么我们可以在应用启动时设置一个默认的未捕获异常处理器。即调用Thread.setDefaultUncaughtExceptionHandler(handler)
  • setDefaultUncaughtExceptionHandler被调用多次如何理解?
    • Thread.setDefaultUncaughtExceptionHandler(handler) 方法如果被多次调用的话,会以最后一次传递的 handler 为准,所以如果用了第三方的统计模块,可能会出现失灵的情况。对于这种情况,在设置默认 hander 之前,可以先通过 getDefaultUncaughtExceptionHandler() 方法获取并保留旧的 hander,然后在默认 handler 的uncaughtException 方法中调用其他 handler 的 uncaughtException 方法,保证都会收到异常信息。

项目地址:https://github.com/yangchong211/YCAndroidTool

03.Android崩溃Crash库之ExceptionHandler分析的更多相关文章

  1. 获取Android崩溃crash信息并写入日志发送邮件

    一.实现Thread.UncaughtExceptionHandlerUnChecked异常发生时,由于没有相应的try…catch处理该异常对象,所以Java运行环境将会终止,程序将退出,也就是我们 ...

  2. Android Native crash日志分析

    在Android应用crash的类型中,native类型crash应该是比较难的一种了,因为大家接触的少,然后相对也要多转几道工序,所有大部分对这个都比较生疏.虽然相关文章也有很多了,但是我在刚开始学 ...

  3. android 程序崩溃crash日志的捕捉

    android 程序崩溃crash日志的捕捉 之前在项目开发过程中,一直会遇到程序崩溃了,但是测试組的哥哥们又没及时的导出日志.... 后来在诳群的时候听别人说起,腾讯有那么一个叫bugly的东西 将 ...

  4. Android Debuggerd 简要介绍和源码分析(转载)

    转载: http://dylangao.com/2014/05/16/android-debuggerd-%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D%E5%92%8C%E ...

  5. android.support.design库的引用和冲突解决

    android.support.design库的引用和冲突解决 转 https://www.jianshu.com/p/2a0a2af9f2b4 最近在工程中使用到android.support.de ...

  6. Android程序crash处理

    Android程序crash处理 时间 2014-11-24 13:45:37  CSDN博客 原文  http://blog.csdn.net/allen315410/article/details ...

  7. android 常见死机问题--log分析

    http://blog.csdn.net/fangchongbory/article/details/7645815         android 常见死机问题--log分析============ ...

  8. 转——Android应用开发性能优化完全分析

    [工匠若水 http://blog.csdn.net/yanbober 转载请注明出处.] 1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉 ...

  9. Android 应用开发性能优化完全分析

    1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉大家你一总结.我一总结的都说到了很多优化注意事项,但是看过这些文章后大多数存在一个问题就是只 ...

  10. 【转】Android应用开发性能优化完全分析

    http://blog.csdn.net/yanbober/article/details/48394201 1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关 ...

随机推荐

  1. typescript json 转 bean

    3个文件: 1.UserInfoGetResponse.ts class UserInfoGetResponse{ private userId: number; private userName: ...

  2. 【.NET】聊聊 IChangeToken 接口

    由于两个月的奋战,导致很久没更新了.就是上回老周说的那个产线和机械手搬货的项目,好不容易等到工厂放假了,我就偷偷乐了.当然也过年了,老周先给大伙伴们拜年了,P话不多讲,就祝大家身体健康.生活愉快.其实 ...

  3. Power BI 7 DAY

    DAX 表达式(Data Analysis Expressions) DAX表达式的结果应用在数据透视表中 DAX表达式的结果作用于整列或者表中所有行 还需注意以下几点: a. 表名用"'' ...

  4. 【Unity3D】激光灯、碰撞特效

    1 需求描述 ​ 本文将模拟激光灯(或碰撞)特效,详细需求如下: 从鼠标位置发射屏幕射线,检测是否与物体发生碰撞 当与物体发生碰撞时,在物体表面覆盖一层激光灯(或碰撞)特效 ​ 本文代码见→激光灯.碰 ...

  5. 开年喜报!Walrus成功入选CNCF云原生全景图

    近日,数澈软件 Seal (以下简称"Seal")旗下开源应用管理平台 Walrus 成功入选云原生计算基金会全景图(CNCF Landscape)并收录至 "App D ...

  6. New Questions

    1. C++/Qt 实现一个信号槽 #include <functional> #include <iostream> #include <vector> clas ...

  7. helloShell

    初识SHELL 变量 常规的变量赋值不必多说,shell脚本还可以从命令输出中提取信息,赋值给变量 反引号字符 testing= `date` $( )格式 testing=$(date) #!/bi ...

  8. ChainMap合并字典

    在python中,我们有两个字典需要合并的时候,可以使用字典的update方法 a = {'a': 1, 'b': 2} b = {'x': 3, 'y': 4} a.update(b) print( ...

  9. java+mysql实现的公益管理系统

    一功能 1.管理员的登录 2.公益项目的增删改查 3.负责人的增删改查 4.捐款人的增删改查 5.志愿者增删改查 二界面展示 1.欢迎界面 2.登录界面 3.系统首页 4.项目管理 5.负责人管理 6 ...

  10. 【LeetCode回溯算法#04】组合总和I与组合总和II(单层处理位置去重)

    组合总和 力扣题目链接(opens new window) 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target ...