Saturday, 5 May 2018

Handling exceptions thrown from ActivityThread in Android


Sometimes it is desired to prevent the default exception handler from terminating application. This may be done via setting custom default uncaught exception handler.

What happens if the caught exception is thrown from ActivityThread? See Android/Sdk/sources/android-26/android/app/ActivityThread.java for the source. When exception is thrown from ActivityThread the Looper.loop() cycle invoked from ActivityThread.main() method terminates. As the exception is caught by custom default exception handler, it does not instruct application to finish. However, application becomes frozen. Main looper no longer processes new messages.

To make application run again, the main looper needs to be revived. This can be done inside the custom exception handler:
    private void setOnErrorFailedExceptionHandler() {
        android.os.Handler handler = new android.os.Handler(Looper.getMainLooper());
        Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
                                        Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {

            if (e instanceof AndroidRuntimeException 
                        /*&& e is a specific exception that needs treatment*/) {
                android.util.Log.e("error", "DefaultUncaughtExceptionHandler", e);
                Looper.loop() // Revive main looper 
            } else if (uncaughtExceptionHandler != null) {
                // All other exceptions should be treated by default
                uncaughtExceptionHandler.uncaughtException(t, e); 
            }
        });
    }


Some edge cases may be solved with this trick. For example, take the introduction of new exception in Android O "android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()". The stacktrace for this exception shows that is is thrown from ActivityThread:
"android.app.ActivityThread$H.handleMessage(ActivityThread.java:1775)"
"android.os.Handler.dispatchMessage(Handler.java:105)"
"android.os.Looper.loop(Looper.java:164)"
"android.app.ActivityThread.main(ActivityThread.java:6541)"
"java.lang.reflect.Method.invoke(Native Method)"
"com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)"
"com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)"


The android.app.RemoteServiceException is a private exception, that subclasses AndroidRuntimeException.

This exception is meant to terminate application, so that the developers should follow the new foreground service rules. If time span between the startForegroundService() and setForegorund() call in Service.onCreate() is longer than ANR period, then this exception is thrown. It was not designed to be caught. There are a few catches though, when terminating application with a crash is undesired, given that "startForegroundService() -> startForeground()" rule is already implemented.

1. During debug sessions this exception fires quite often, because of slower emulator - or just the breakpoints being hit.
2. When released, the actual user device may lag in the very unfortunate moment between those calls, and app will be hit with the crash.

Especially in the second case it would be much better to retry the service start, or opt it to be started later, instead of crashing app altogether. Catching such exception and reviving main loop afterwards is possible with this approach.

9 comments :

  1. Where do you call setOnErrorFailedExceptionHandler() ?

    ReplyDelete
    Replies
    1. Preferably as soon as possible, for example in Application onCreate().

      Delete
  2. Hi , what is the purpose of this line
    android.os.Handler handler = new android.os.Handler(Looper.getMainLooper());

    ReplyDelete
    Replies
    1. Sorry, it is not used in provided snippet. But this handler can be used inside exception handler to provide some feedback for ui, for example show toast with the call to handler.postDelayed().

      Delete
  3. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.

    Devops training in velachery
    Devops training in annanagar
    Devops training in tambaram
    DevOps online Training

    ReplyDelete
  4. You got an extremely helpful website I actually have been here reading for regarding an hour. I’m an initiate and your success is incredibly a lot of a concept on behalf of me.
    Blueprism training in velachery

    Blueprism training in marathahalli

    ReplyDelete
  5. You got an extremely helpful website I actually have been here reading for regarding an hour. I’m an initiate and your success is incredibly a lot of a concept on behalf of me.

    angularjs Training in bangalore

    angularjs Training in btm

    angularjs Training in electronic-city

    angularjs online Training

    angularjs Training in marathahalli

    ReplyDelete
  6. Hmm, it seems like your site ate my first comment (it was extremely long) so I guess I’ll just sum it up what I had written and say, I’m thoroughly enjoying your blog. I as well as an aspiring blog writer, but I’m still new to the whole thing. Do you have any recommendations for newbie blog writers? I’d appreciate it.

    AWS Interview Questions And Answers

    AWS Training in Bangalore | Amazon Web Services Training in Bangalore

    AWS Training in Pune | Best Amazon Web Services Training in Pune

    Amazon Web Services Training in Pune | Best AWS Training in Pune

    AWS Online Training | Online AWS Certification Course - Gangboard

    ReplyDelete