javaandroidexceptionuncaught-exceptionuncaughtexceptionhandler

Thread's DefaultUncaughtExceptionHandler not catching exception thrown in AlertDialog onClick() method


I set a default UncaughtExceptionHandler for the current thread in my Activity's onCreate() and throw a RunTimeException from my AlertDialog's onClick() listener but the exception is not caught. I also tried throwing the exception from onCreateOptionsMenu() and onOptionsItemSelected() and it wasn't caught. It is caught however when thrown from onCreate() or onResume().

Here's my activity:

public class MainActivity extends AppCompatActivity implements DialogInterface.OnClickListener {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Thread.setDefaultUncaughtExceptionHandler(new DefExcHandler());
        Log.d(TAG, "onCreate: "+Thread.currentThread().getName()+" "+Thread.currentThread().getId());

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setNeutralButton("CRASH", this);
        AlertDialog dialog = builder.create();
        dialog.show();
        dialog.setCancelable(false);
        dialog.setCanceledOnTouchOutside(false);
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        dialog.dismiss();
        Log.d(TAG, "onClick: "+Thread.currentThread().getName()+" "+Thread.currentThread().getId());
        throw new RuntimeException();
    }
}

And here's my exception handler:

public class DefExcHandler implements Thread.UncaughtExceptionHandler {

    private static final String TAG = "DefExcHandler";

    public DefExcHandler() {
        Log.d(TAG, "DefaultExceptionHandler: ");
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        Log.d(TAG, "Uncaught exception caught: "+ex.getMessage()+" "+ex.getCause().toString());
    }
}

Based on the log messages I make in my activity the thread is the same when in onCreate() and onClick():

09-27 15:00:57.260 10667-10667/a.b.c D/MainActivity: onCreate: main 1
09-27 15:01:52.680 10667-10667/a.b.c D/MainActivity: onClick: main 1

Why is the exception not caught when thrown from onClick(), onCreateOptionsMenu() or onOptionsItemSelected()?


Solution

  • I tried to reproduce the application's behavior. The DefExcHandler instance seems to be missed since it isn't logging anything, but I tried two other things that worked: breakpoint and file creation.

    Please try to set a breakpoint inside uncaughtException(). I believe it will stop the execution in your code. And I have written an example below that uses a file as a flag to indicate that the uncaught exception handler is really being invoked.

    In sum, it seems that DefExcHandler.uncaughtException() is being called but Android can't do the logging for some unknown reason, maybe an internal race condition.

    // Example that assures that an UncaughtExceptionHandler
    // is being invoked by flagging execution with file creation.
    
    public class MainActivity extends ... implements ... {
    
        private File mCrashFile;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
    
            mCrashFile = new File(getFilesDir(), "crash");
    
            // Crash file existence means the application has crashed before.
            Log.d(TAG, "crash file exists: " + mCrashFile.exists());
    
            // Clear crash flag.
            if (mCrashFile.exists()) {
                Log.d(TAG, "crash file delete: " + mCrashFile.delete());
            }
    
            Thread.setDefaultUncaughtExceptionHandler(new DefExcHandler());
    
            ...
    
        }
    
        private class DefExcHandler implements Thread.UncaughtExceptionHandler {
    
            @Override
            public void uncaughtException(Thread thread, Throwable ex) {
    
                // Set crash flag.
                try {
                    mCrashFile.createNewFile();
                } catch (IOException e) {
                    // dunno if it really prints anything
                    e.printStackTrace();
                }
            }
        }
    }