安卓如何捕获未处理异常并显示对话框

4

我希望在我的应用程序中处理未处理的异常,而不需要使用任何第三方库。

因此,我编写了一段代码。

活动:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Thread.setDefaultUncaughtExceptionHandler(new ReportHelper(this));
    throw new NullPointerException();
}

我的崩溃处理程序:

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.widget.Toast;

/**
 * Created by S-Shustikov on 08.06.14.
 */
public class ReportHelper implements Thread.UncaughtExceptionHandler {
    private final AlertDialog dialog;
    private       Context     context;

    public ReportHelper(Context context) {
        this.context = context;
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage("Application was stopped...")
                .setPositiveButton("Report to developer about this problem.", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {

                    }
                })
                .setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // Not worked!
                        dialog.dismiss();
                        System.exit(0);
                        android.os.Process.killProcess(android.os.Process.myPid());

                    }
                });

        dialog = builder.create();
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        showToastInThread("OOPS!");
    }

    public void showToastInThread(final String str){
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(context, "OOPS! Application crashed", Toast.LENGTH_SHORT).show();
                if(!dialog.isShowing())
                    dialog.show();
                Looper.loop();

            }
        }.start();
    }
}

当我像你所看到的那样启动应用程序时,我抛出了NullPointerException。在我的处理逻辑中,Toast被显示,并且对话框也被显示了。但是!对话框点击没有正确处理。我的意思是onClick方法中的逻辑没有工作。问题出在哪里,我该如何解决?


请定义什么是不正确的?在您的onClick行为中发生了什么,期望的行为是什么? - Martin Konecny
你期望看到什么?你看到了什么?也就是说,为什么你会说“它不起作用”? - ben75
@SergeyShustikov,我也遇到了这个问题。你找到任何解决方案了吗? - Naruto Uzumaki
很遗憾,@NarutoUzumaki,我不能这样做。我会在应用程序完成之前显示有关错误的提示,并将标志保存到首选项中。当用户下次启动应用程序时,我会显示对话框,通知上次应用程序启动时崩溃,并建议用户向我发送错误报告。 - Sergey Shustikov
@SergeyShustikov 谢谢回复。我找到了解决办法。请看下面的答案。 - Naruto Uzumaki
显示剩余2条评论
3个回答

5

在我的情况下,我将 AlertDialog.Builder 移动到线程运行函数中,如下所示:

public void showToastInThread(final String str){
    new Thread() {
        @Override
        public void run() {
            Looper.prepare();

            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setMessage("Application was stopped...")
            .setPositiveButton("Report to developer about this problem.", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {

                }
            })
            .setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    // Not worked!
                    dialog.dismiss();
                    System.exit(0);
                    android.os.Process.killProcess(android.os.Process.myPid());

                }
            });

            dialog = builder.create();


            Toast.makeText(context, "OOPS! Application crashed", Toast.LENGTH_SHORT).show();
            if(!dialog.isShowing())
                dialog.show();
            Looper.loop();

        }
    }.start();
}

一切都完美地运行。希望这可以帮助你。

你确定你只处理了一次崩溃操作吗?因为我捕获了2-3个异常。 - Sergey Shustikov
当第一个异常发生时,我会看到警报对话框,并使用关闭按钮侦听器来终止我的应用程序。你说的2-3个异常是什么意思? - Naruto Uzumaki
我的意思是ARM向我们的异常处理程序发送了2-3次“uncaughtException(Thread thread,Throwable ex)”。 - Sergey Shustikov

2
根据这篇文章,当调用setDefaultUncaughtExceptionHandler时,应用程序的状态是未知的。这意味着您的onClick监听器可能不再处于活动状态。
为什么不使用这种方法:
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        setContentView(R.layout.main);
        Thread.setDefaultUncaughtExceptionHandler(new ReportHelper(this));
        throw new NullPointerException();
    } catch (NullPointerException e) {
        new ReportHelper(this);
    }
}

删除实现Thread.UncaughtExceptionHandler接口的ReportHelper。

不显式捕获异常的方法可以被视为反模式


但点击事件没有被处理。我在调试中检查了这个问题。 - Sergey Shustikov
1
如果我删除我的ReportHelper,那么我将会得到一个编译错误,因为Thread.setDefaultUncaughtExceptionHandler传递了实现Thread.UncaughtExceptionHandler的参数。我希望你没有忽略这一点。 - Sergey Shustikov
我将日志放入了uncaughtException(Thread thread, Throwable ex)中,然后看到了神奇的行为。这个方法从不同的线程中调用了3次。 - Sergey Shustikov

0

由于在 UI 线程上发生了异常:该线程的状态可能是 意外的

因此,请在单击处理程序中尝试简单地执行以下操作:

.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    android.os.Process.killProcess(android.os.Process.myPid());

                }
            });

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