Using Global Exception Handling on android

Basic Example for someone who comes to this page with a solution :)

public class ChildActivity extends BaseActivity {
    @SuppressWarnings("unused")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        int a=1/0;
    }
}

Class for handling error:

public class BaseActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
                Log.e("Alert","Lets See if it Works !!!");
            }
        });
    }
}

Here's a variant of the answer by Mohit Sharma with the following improvements:

  • Doesn't cause the app/service to freeze after error handling
  • Lets Android do its normal error handling after your own

Code:

public class BaseActivity extends Activity {
    @Override
    public void onCreate() {
        super.onCreate();

        final Thread.UncaughtExceptionHandler oldHandler =
            Thread.getDefaultUncaughtExceptionHandler();

        Thread.setDefaultUncaughtExceptionHandler(
            new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(
                    Thread paramThread,
                    Throwable paramThrowable
                ) {
                    //Do your own error handling here

                    if (oldHandler != null)
                        oldHandler.uncaughtException(
                            paramThread,
                            paramThrowable
                        ); //Delegates to Android's error handling
                    else
                        System.exit(2); //Prevents the service/app from freezing
                }
            });
    }
}

For those who just want to see exception details when your app crashes on device (in debug config). This is application class:

private Thread.UncaughtExceptionHandler oldHandler;

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

    if (!BuildConfig.DEBUG)
        return;

    oldHandler = Thread.getDefaultUncaughtExceptionHandler();
    Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
        try {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));

            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.putExtra(Intent.EXTRA_TEXT, sw.toString());
            intent.setType("text/plain");
            startActivity(intent);
        } catch(Exception ex) {
            ex.printStackTrace();
        } finally {
            if (oldHandler != null)
                oldHandler.uncaughtException(t, e);
            else
                System.exit(1);
        }
    });
}

It uses external app as your UI thread might not working anymore.


Keep in mind that the The RuntimePermission("setDefaultUncaughtExceptionHandler") is checked prior to setting the handler and make sure you cause the process to halt afterwards, by throwing an uncaught exception, as things could be in an uncertain state.

Do not display anything, indeed the UI thread might have been the one that crashed, do write a log and/or send the details to a server, instead. You might want to check out this question and its answers.


I just wanted to point out my experience so far. I am using the solution suggested by https://stackoverflow.com/a/26560727/2737240 to flush the exception into my log file before giving control to the default exception handler.

However, my structure looks like this:

          BaseActivity
               |
    _______________________
    |          |          |
Activity A Activity B Activity C
final Thread.UncaughtExceptionHandler defaultEH = Thread.getDefaultUncaughtExceptionHandler();                                                                                                                                                                                                                                                                                                                              
    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {                                                                                                                                                                                                                                                                                                                                           
        @Override                                                                                                                                                                                                                                                                                                                                                                                                               
        public void uncaughtException(Thread thread, Throwable e) {                                                                                                                                                                                                                                                                                                                                                             
            handleUncaughtException(thread, e, defaultEH);                                                                                                                                                                                                                                                                                                                                                                      
        }                                                                                                                                                                                                                                                                                                                                                                                                                       
 });

where handleUncaughtException(thread, e, defaultEH); writes to the log and hands the call over to the original UncaughtExceptionHandler.

So what happened by using this code was the following:

  • Activity A is instantiated
  • New Default Exception Handler (DEH) is now my log handler + the old DEH
  • Activity B is instantiated
  • New DEH is now my log handler + the old DEH (log handler + original DEH)
  • Activity C is instantiated
  • New DEH is now my log handler + the old DEH (log handler + log handler + original DEH)

So it's a chain growing infinitely causing two problems:

  1. The specified custom code (in my case writing to the log file) will be called multiple times, which is not the desired action.
  2. The reference of defaultEh is kept in the heap even after the activity has been finished, because it is used by the reference chain so the worst thing that could happen is an out of memory exception.

Therefore I added one more thing to finally make this work without issues:

private static boolean customExceptionHandlerAttached = false;                                                                                                                                                                                                                                                                                                                                                                      

@Override                                                                                                                                                                                                                                                                                                                                                                                                                           
protected void onCreate(@Nullable Bundle savedInstanceState) {                                                                                                                                                                                                                                                                                                                                                                      
    super.onCreate(savedInstanceState);                                                                                                                                                                                                                                                                                                                                                                                             

    if(!customExceptionHandlerAttached) {                                                                                                                                                                                                                                                                                                                                                                                            
        final Thread.UncaughtExceptionHandler defaultEH = Thread.getDefaultUncaughtExceptionHandler();                                                                                                                                                                                                                                                                                                                              
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {                                                                                                                                                                                                                                                                                                                                           
            @Override                                                                                                                                                                                                                                                                                                                                                                                                               
            public void uncaughtException(Thread thread, Throwable e) {                                                                                                                                                                                                                                                                                                                                                             
                 handleUncaughtException(thread, e, defaultEH);                                                                                                                                                                                                                                                                                                                                                                      
            }                                                                                                                                                                                                                                                                                                                                                                                                                       
        });                                                                                                                                                                                                                                                                                                                                                                                                                         
        customExceptionHandlerAttached = true;                                                                                                                                                                                                                                                                                                                                                                                      
    }                                                                                                                                                                                                                                                                                                                                                                                                                               
}

With this solution we can make sure to:

  • add a custom exception handler for our desired action
  • ensure that this action is only triggered once
  • allowing garbage collector to dispose our activity completely by calling finish()