记录日志后重新抛出未捕获的异常处理器异常

9
在我的Application类中,我试图在发生强制关闭之前捕获它,这样我就可以记录下来,然后重新抛出它,以便Android系统处理。我这么做是因为有些用户不报告强制关闭。
我在使用Eclipse开发时,它不允许我重新抛出异常。它显示一个错误,提示“未处理的异常类型Throwable:用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();
    }
}

}

4个回答

15
抱歉,我不是Android专家-但看起来你不能抛出异常,因为你的方法签名“void uncaughtException(Thread,Throwable)”没有声明它会“throws”任何东西。
假设你正在重写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);
        }
    });

嗯,我认为你是对的。我不熟悉装饰器模式,你能展示一些代码给我看看如何实现吗? - Ryan R
是的,那起作用了!谢谢麻雀船长。(注意:弹出提示框未显示。) - Ryan R
有点晚了,但我刚使用Capn Sparrow的解决方案,我应该指出toast没有显示,因为在makeText调用的末尾没有.show()。这经常发生在我的身上。 - AJ87uk

1
对于现在才发现这个问题的任何人,我不确定以上任何答案在发布时是否正确,但现在它们已经不正确了。现在你想要的是创建一个实现 Thread.UncaughtExceptionHandler 的新类,在构造函数中保存 Thread.getCurrentUncaughtExceptionHandler() 到一个字段中,然后在你的 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()方法),如果由于某种原因你决定不再运行代码。

0

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

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

不太符合我的要求,因为我想要一个“万能”的东西,在强制关闭我的应用程序之前记录任何异常。我使用try/catch,但以防万一有未捕获的异常。 - Ryan R

0

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

此外,您可以尝试在退出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();
        }
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接