记录后重新抛出 UncaughtExceptionHandler 异常

Posted

技术标签:

【中文标题】记录后重新抛出 UncaughtExceptionHandler 异常【英文标题】:Rethrow UncaughtExceptionHandler Exception after Logging It 【发布时间】:2012-02-21 11:38:34 【问题描述】:

在我的应用程序类中,我试图在它发生之前捕捉一个力关闭,所以我可以记录它然后重新抛出它,以便 android 可以处理它。我这样做是因为一些用户不报告强制关闭。

我在eclipse中开发,eclipse不允许我重新抛出异常。它显示一条错误消息“未处理的异常类型 Throwable: Surround with try/catch”。如何重新抛出异常?

public class MainApplication extends Application

    @Override
    public void onCreate()
    
    super.onCreate();

    try
    
        //Log exception before app force closes
        Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() 
            @Override
            public void uncaughtException(Thread thread, Throwable ex) 
                AnalyticsUtils.getInstance(MainApplication.this).trackEvent(
                        "Errors",                       // Category
                        "MainActivity",                 // Action
                        "Force Close: "+ex.toString(),  // Label
                        0);                             // Value
                AnalyticsUtils.getInstance(MainApplication.this).dispatch();

                Toast.makeText(MainApplication.this, "Snap! Something broke. Please report the Force Close so I can fix it.", Toast.LENGTH_LONG);

                //rethrow the Exception so user can report it
                //throw ex; //<-- **eclipse is showing an error to surround with try/catch**
            
        );

     catch (Exception e)
    
        e.printStackTrace();
    

【问题讨论】:

【参考方案1】:

抱歉,不是 Android 专家 - 但看起来您不能抛出 ex,因为您的方法签名“void uncaughtException(Thread, Throwable)”没有声明它“抛出”任何东西。

假设您要覆盖 API 接口,并且 (a) 不能修改此签名,并且 (b) 不想修改,因为您会将其脱离上下文,您是否可以改用装饰器模式,并且基本上子类化默认的 UncaughtExceptionHandler 实现来记录您的消息,然后让它像往常一样继续处理?

编辑:未经测试,但这可能有点像:

    final UncaughtExceptionHandler subclass = Thread.currentThread().getUncaughtExceptionHandler();
    Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() 
        @Override
        public void uncaughtException(Thread thread, Throwable ex) 
            // your code 
            AnalyticsUtils.getInstance(MainApplication.this).trackEvent(
                    "Errors",                       // Category
                    "MainActivity",                 // Action
                    "Force Close: "+ex.toString(),  // Label
                    0);                             // Value
            AnalyticsUtils.getInstance(MainApplication.this).dispatch();
            Toast.makeText(MainApplication.this, "Snap! Something broke. Please report the Force Close so I can fix it.", Toast.LENGTH_LONG).show();

            // carry on with prior flow
            subclass.uncaughtException(thread, ex);
        
    );

【讨论】:

嗯,我认为你是对的。我不熟悉装饰器模式,你能展示一些代码来告诉我如何做到这一点吗? 是的,成功了!谢谢船长麻雀。 (注意:吐司不显示。) 这里有点晚了,但是因为我刚刚使用了 Capn Sparrow 的解决方案,所以我应该指出 toast 没有显示,因为在 makeText 调用结束时现在有 .show() 。这让我很频繁。【参考方案2】:

我不确定这是否是您想要的,但是您可以添加一个finally 子句,该子句将在成功的try 或已处理的catch 之后运行:

try 
    //your code
 catch (Exception e) 
    //print stack trace
 finally 
    //Log

【讨论】:

不是我想要的,因为我想要一个“全部捕获”,它会在强制关闭我的应用程序之前记录任何异常。我使用 try/catch,但以防万一有什么东西没被抓到。【参考方案3】:

我认为下面的代码应该可以工作。使用新线程执行处理程序代码允许应用程序显示警报/toast 并执行其他操作。

另外,您可以尝试在退出 run() 之前调用 System.exit(0)。这将重新启动最后一个活动。

我在 Ginger BreadJelly Bean 上都检查了这一点。

final UncaughtExceptionHandler subclass = Thread.currentThread().getUncaughtExceptionHandler();
    Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() 
        @Override
        public void uncaughtException(Thread thread, Throwable ex) 
            new Thread() 
                @Override
                public void run() 
                    Looper.prepare();  
                    // your code 
                    AnalyticsUtils.getInstance(MainApplication.this).trackEvent(
                            "Errors",                       // Category
                            "MainActivity",                 // Action
                            "Force Close: "+ex.toString(),  // Label
                            0);                             // Value
                    AnalyticsUtils.getInstance(MainApplication.this).dispatch();
                    Toast.makeText(MainApplication.this, "Snap! Something broke. Please report the Force Close so I can fix it.", Toast.LENGTH_LONG);

                    Looper.loop();
                
            .start();
        
    );

【讨论】:

【参考方案4】:

对于现在才发现这个问题的任何人,我不确定上述任何答案在发布时是否正确,但现在不是。你现在想要的是创建一个实现Thread.UncaughtExceptionHandler 的新类,并在构造函数中,将Thread.getCurrentUncaughtExceptionHandler() 保存到一个字段中,然后在你的末尾调用its uncaughtException(Thread, Throwable) uncaughtException(Thread, Throwable) 方法。然后你从任何代码实例化你的类调用Thread.setUncaughtExceptionHandler(),通常是MainApplication.onCreate

所以完整的代码看起来像这样:

//MainApplication.java
public class MainApplication extends Application 
    @Override
    public void onCreate() 
        super.onCreate();
        CustomUncaughtExceptionHandler cueh = new CustomUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(cueh);
    


//CustomUncaughtExceptionHandler.java
class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler 
    private Thread.UncaughtExceptionHandler oldHandler;

    CustomUncaughtExceptionHandler() 
        oldHandler = Thread.getDefaultUncaughtExceptionHandler();
    

    @Override
    public void uncaughtException(Thread thread, Throwable ex) 
        AnalyticsUtils.getInstance(MainApplication.this).trackEvent(
                "Errors",                       // Category
                "MainActivity",                 // Action
                "Force Close: "+ex.toString(),  // Label
                0);                             // Value
        AnalyticsUtils.getInstance(MainApplication.this).dispatch();
        if (oldHandler != null) 
            oldHandler.uncaughtException(thread, ex);
        else 
            System.exit(1);
        
    

如果出于某种原因您决定不再希望代码运行,您还可以将原始默认的未捕获异常处理程序存储在其他位置以便稍后恢复(或添加 getOldHandler() 方法)。

【讨论】:

以上是关于记录后重新抛出 UncaughtExceptionHandler 异常的主要内容,如果未能解决你的问题,请参考以下文章

声纳抱怨记录或重新抛出异常

Azure 函数应该记录错误还是抛出异常?

重新抛出遇到的异常后,使用块中的sql连接状态会怎样?

如何找出PHP代码块可能抛出的所有错误?

发生 express 错误时,pm2 不会重新启动 worker

4.异常捕获后再次抛出