Eclipse - 在 Android 应用程序上引发未处理的异常时中断用户代码

Posted

技术标签:

【中文标题】Eclipse - 在 Android 应用程序上引发未处理的异常时中断用户代码【英文标题】:Eclipse - break on user code when unhandled exception is raised on Android App 【发布时间】:2016-10-19 19:23:14 【问题描述】:

我的问题很简单:

我使用 Ecplise(Luna 或 Neon)在 android 上进行开发,我不想使用 Android Studio

我希望仅在导致异常的堆栈的最后一个用户代码调用上调试 ALL 未处理异常的中断(因此,例如,我不想中断一个无用的 ZygonteInit&MethodAndArgsCaller.run(),当由于将空引用传递给原生 Android SDK 方法而导致异常时)。

我知道我可以在断点视图中为特定异常设置断点 (NullPointerException..Throwable...),但我想在所有未处理的情况下中断。 我知道我可以通过在 Java 调试选项中设置“步骤过滤器”来过滤调试,但在我的情况下,这不适用于所有异常。

编辑

在我的堆栈下方的图像中,当引发异常时(在我的代码中除以零)

如果我在引发异常后设置了默认的未捕获异常处理程序,则主线程的堆栈。

【问题讨论】:

我会尝试执行以下操作:1. 实现自己的自定义 UncoughtExceptionHandler(请参阅Using Global Exception Handling on android)和 2. 在该处理程序上使用条件断点 - 对于条件检查堆栈跟踪是否包含你的一个类。 @Robert 感谢您提出的解决方案。我确实尝试过这个解决方案,但问题是 UncoughtExceptionHandler 由另一个线程处理,而导致异常的线程刚刚结束并且在堆栈上不可用。 @christianmini: 线程在堆栈上可用吗?那句话有问题 @Thomas。如果我在 UncaughtExceptionHandler 中设置断点,则主线程的堆栈(在 Eclipse 调试视图中可见)包含此调用作为叶子和 ThreadGroup.uncaughtException(Thread, Throwable) 作为根,但不包含导致异常的方法。 @christianmini:AFAIK,这对于未处理的异常处理程序是不可能的。我在 C++ 和 C# 中都没有看到过这种情况,所以我怀疑在 Java 中是否可行。我真的很想知道为什么打破“异常”和“可投掷”是不够的。隐藏了哪些异常?例如,在 C# 中,您无法捕获 ***Exception(可能还有更多)。 【参考方案1】:

你可以先在Eclipse中验证这个设置是否启用。

窗口 -> 首选项 -> Java -> 调试 -> 对未捕获的异常暂停执行

如果启用此设置,任何未捕获的异常都将在 JVM 被抛出的那一刻暂停,包括使用反射调用的类。这是没有添加任何断点,但提供了它的未处理,即您的代码甚至不会被来自 try-catch 的外部代码调用。

例如

int a = 0, b= 0;
System.out.println(a/b); // ArithmeticException

即使这段代码是从反射调用代码中调用的,eclipse 也会在 sysout 处挂起,所有变量在堆栈上仍然可用。

但是在 Android 的启动类 ZygoteInit 中有这样一行:

    catch (Throwable t) 
                Log.e(TAG, "Error preloading " + line + ".", t);
                if (t instanceof Error) 
                    throw (Error) t;
                
                if (t instanceof RuntimeException) 
                    throw (RuntimeException) t;
                
                throw new RuntimeException(t);
            

此类代码会破坏 Eclipse 调试的原因是,RuntimeException现在不再未处理。您的 UncaughtExceptionHandler 实际上可能正在捕获启动类而不是您的用户代码。这是用于常规 Eclipse。

解决方案 1:

    转到运行 -> 添加 Java 异常断点 -> Throwable 在断点视图中单击Throwable 右键->断点属性->添加包->确定 检查选项此异常的子类

注意:这可以勉强捕捉到java.lang.OutOfMemoryError,但绝对不能捕捉到java.lang.***Error

解决方案 2:仅当捕获的异常过多时,否则不推荐

    com.android.internal.os.ZygoteInit的源代码复制到一个新项目比如MyBootstrap

    修改catch (Throwable t) 块以仅捕获Error

         catch (Error t) 
            Log.e(TAG, "Error preloading " + line + ".", t);
            throw t;
        
    

    转到调试配置 -> 类路径 -> 单击引导条目 -> 添加项目 -> MyBootstrap。将此项目移至顶部

【讨论】:

我想你理解了这个问题:“RuntimeException 现在不再未处理”,所以我必须从所有 Java 运行时异常中选择父级,“Throwable”,并且我必须选择“Caught”位置” e “此异常的子类”。好的,唯一的问题是在一个Android应用程序的周期中,用户代码或SDK中有数百个Caught Exception,有没有办法过滤? @christianmini 那么在这种情况下,catch 不能是确定性的。删除catch (Throwable t) 以进行调试如何引导ZygoteInit?由于此类用于启动并且不进行本机调用,因此对于调试应该没问题。这样,您未处理的异常仍然未处理。 好的,我也会更新答案以包括引导程序,以防不熟悉的人。【参考方案2】:

基本上,如果我对您的理解正确,您希望设置一个断点,如果该异常没有/以后不会被处理,则该断点将触发在引发异常的点

如果这就是你的意思,那么你所要求的基本上是不可能的。

    在抛出异常时,调试器无法判断异常是否会被捕获。

    在捕获异常的点,从抛出点...到捕获点...的状态(即堆栈帧、变量等)将被丢弃。

    Java 调试器 API 不支持调试器可用于此目的的“倒带和重放”机制。


在我看来,您能做的最好的事情是 1) 识别您怀疑没有被捕获的异常,2) 在其构造函数或合适的超类构造函数上设置断点,3) 找出一些过滤条件排除不感兴趣的情况,并 4) 单步执行代码以查看是否捕获到异常。

注意:异常可能在与其实例化的不同点被抛出或重新抛出,因此异常构造函数断点并不总是有帮助。但通常会。

【讨论】:

我认为很少有库有 catch Throwable 出于真正的原因,但隐藏在深处,导致调试过程中出现这种歧义。可能是类似的通用案例应该去 Java/Exception 的 SO 文档。 我的答案没有谈到捕获异常。而且我想不出创建Throwable 实例的正当理由。 关于 SO 文档的建议......我们还没有任何关于 Java 调试技术的重要信息。 (除了一个关于如何读取堆栈跟踪的示例。)

以上是关于Eclipse - 在 Android 应用程序上引发未处理的异常时中断用户代码的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有 Gradle 的 Eclipse 中的 Android 项目上启用 multidex

如何在 Eclipse 构建的 Android 应用程序中删除屏幕上的键

Android NDK 创建可执行文件但未将其推送到设备上 (Eclipse)

eclipse怎样运行android程序

如何在 Eclipse 中为 android 调试 javafx 应用程序

Android 和 Eclipse:更改应用程序 API 级别