显示在'Thread.setDefaultUncaughtExceptionHandler`对话框对话框、Thread、setDefaultUncaughtExceptionHandler

2023-09-03 22:32:25 作者:伤你最深的人偏得你心

在我的Andr​​oid应用程序抛出一个异常,我想显示自定义对话框,告诉用户有什么不对的事,所以我用 Thread.setDefaultUncaughtExceptionHandler 来设置一个全局异常处理程序:

When my android application throw an exception, I want to show a custom dialog to tell user there is something wrong happened, so I use Thread.setDefaultUncaughtExceptionHandler to set a global exception handler:

public class MyApplication extends Application {

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

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, final Throwable ex) {
                AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
                builder.setTitle("There is something wrong")
                        .setMessage("Application will exit:" + ex.toString())
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {

                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // throw it again
                                throw (RuntimeException) ex;
                            }
                        })
                        .show();
            }
        });
    }

}

但我发现它有抛出的异常,在 AlertDialog 不会出现,相反,应用程序块,一段时间后,它会显示一个系统对话框:

But I found it there is any exception thrown, the AlertDialog won't show, instead, the application blocks and after a while, it will show a system dialog:

X app is not responding. Would you like to close it?
Wait  |  OK

我应该怎么做呢?

What should I do now?

更新

日志:

11-16 12:54:16.017: WARN/WindowManager(90): Attempted to add window with non-application token WindowToken{b38bb6a8 token=null}.  Aborting.

这似乎错误是来自新AlertDialog.Builder(getApplicationContext());

但是,这是一个异常处理程序应用程序的子类,我怎么可以设置一个活动实例呢?

But this is an exception handler in Application subclass, how can I set an activity instance to it?

推荐答案

您不能在这里做任何UI操作。刚开始另一项活动/闪屏。传递一个额外的意图表示崩溃,并在活动显示对话框。

You cannot do any UI operation from here. Just start another activity/ splash screen. Pass an intent extra to denote crash and show dialog in that activity.

    /*
     * (non-Javadoc)
     * 
     * @see
     * java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.
     * lang.Thread, java.lang.Throwable)
     */
    @Override
    public void uncaughtException(Thread t, final Throwable e) {
        StackTraceElement[] arr = e.getStackTrace();
        final StringBuffer report = new StringBuffer(e.toString());
        final String lineSeperator = "-------------------------------\n\n";
        report.append(DOUBLE_LINE_SEP);
        report.append("--------- Stack trace ---------\n\n");
        for (int i = 0; i < arr.length; i++) {
            report.append( "    ");
            report.append(arr[i].toString());
            report.append(SINGLE_LINE_SEP);
        }
        report.append(lineSeperator);
        // If the exception was thrown in a background thread inside
        // AsyncTask, then the actual exception can be found with getCause
        report.append("--------- Cause ---------\n\n");
        Throwable cause = e.getCause();
        if (cause != null) {
            report.append(cause.toString());
            report.append(DOUBLE_LINE_SEP);
            arr = cause.getStackTrace();
            for (int i = 0; i < arr.length; i++) {
                report.append("    ");
                report.append(arr[i].toString());
                report.append(SINGLE_LINE_SEP);
            }
        }
        // Getting the Device brand,model and sdk verion details.
        report.append(lineSeperator);
        report.append("--------- Device ---------\n\n");
        report.append("Brand: ");
        report.append(Build.BRAND);
        report.append(SINGLE_LINE_SEP);
        report.append("Device: ");
        report.append(Build.DEVICE);
        report.append(SINGLE_LINE_SEP);
        report.append("Model: ");
        report.append(Build.MODEL);
        report.append(SINGLE_LINE_SEP);
        report.append("Id: ");
        report.append(Build.ID);
        report.append(SINGLE_LINE_SEP);
        report.append("Product: ");
        report.append(Build.PRODUCT);
        report.append(SINGLE_LINE_SEP);
        report.append(lineSeperator);
        report.append("--------- Firmware ---------\n\n");
        report.append("SDK: ");
        report.append(Build.VERSION.SDK);
        report.append(SINGLE_LINE_SEP);
        report.append("Release: ");
        report.append(Build.VERSION.RELEASE);
        report.append(SINGLE_LINE_SEP);
        report.append("Incremental: ");
        report.append(Build.VERSION.INCREMENTAL);
        report.append(SINGLE_LINE_SEP);
        report.append(lineSeperator);

        Log.e("Report ::", report.toString());
        Intent crashedIntent = new Intent(BaseActivity.this, SplashActivity.class);
        crashedIntent.putExtra(EXTRA_CRASHED_FLAG,  "Unexpected Error occurred.");
        crashedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
        crashedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(crashedIntent);

        System.exit(0);
        // If you don't kill the VM here the app goes into limbo

    }

另请参见:

Android UncaughtExceptionHandler的实例化一个AlertDialog打破

Toast没有出现在UncaughtExceptionHandler的了

How从UncaughtExceptionHandler的启动活动,如果这是主线程崩溃?

我如何做到这一点:

我有一个BaseActivity延伸活动,并在活动的onCreate我设置的UncaughtExceptionHandler。我所有的活动扩大活动的BaseActivity代替。

I have a BaseActivity which extends Activity, and in onCreate of the activity I set the UncaughtExceptionHandler. All my activities extend the BaseActivity instead of Activity.

您不能设置在异常处理程序 Application.onCreate ,相反,你应该创建一个 BaseActivity 和在它的的onCreate 方法对其进行设置。 启动SplashActivity后,我们应该叫 System.exit(0) 我们不能持有错误的实例分享给 SplashActivity ,因为它会被破坏,相反,我们可以通过一些错误信息,或将其保留在文件中。 You can't set the exception handler in Application.onCreate, instead, you should create a BaseActivity and set it on the onCreate method of it. After starting the SplashActivity, we should call System.exit(0) We can't hold the error instance to share it to SplashActivity, since it will be destroyed, instead, we can pass some error message or persist it in file.