3
minute read
Jamie Lynch

Error handling on Android part 2: implementing an UncaughtExceptionHandler in a JVM app

Error handling on Android

How many times have you been in the middle of using a new shiny app, only to have it crash on you?

Android app displaying a crash dialog

This is the second in a series of posts that will investigate how the exception handling mechanism works in Java and Android, and how crash reporting SDKs can capture diagnostic information, so that you're not flying blind in production.

How do I implement a custom UncaughtExceptionHandler for a JVM app?

We previously learnt how an [CODE]UncaughtExceptionHandler[/CODE] allows us to handle uncaught exceptions in JVM applications. Our goal now is to create a simple handler that captures the stacktrace for every unhandled error, and generates a diagnostic report that could be sent to an error reporting API.

Implementing a basic UncaughtExceptionHandler

We'll start by implementing an [CODE]UncaughtExceptionHandler[/CODE], and setting it as the default handler for all exceptions in the JVM. We'll implement the [CODE]UncaughtExceptionHandler[/CODE] interface, then call [CODE]Thread.setDefaultUncaughtExceptionHandler[/CODE] to override the JVM's default implementation:

-- CODE language-kotlin --
fun main() {
   val exceptionHandler = SimpleExceptionHandler()
   Thread.setDefaultUncaughtExceptionHandler(exceptionHandler)
   throw RuntimeException("Whoops!")
}

class SimpleExceptionHandler : Thread.UncaughtExceptionHandler {
   override fun uncaughtException(thread: Thread, exc: Throwable) {
       // TODO generate a diagnostic report
   }
}

Of course, [CODE]SimpleExceptionHandler[/CODE] isn't much use in its current form, as there isn't currently any handling code in our handler. Our next step will be to obtain a stacktrace from the [CODE]Throwable[/CODE] object, and gather other information to form a diagnostic report.

Capturing stacktrace information for error reports

Right off the bat we'll start off by encapsulating the stacktrace, so that we can capture additional metadata that may be useful in debugging our error. We'll do this by creating a [CODE]Report[/CODE] class which can hold many arbitrary fields:

-- CODE language-kotlin --
fun main() {
   val exceptionHandler = SimpleExceptionHandler()
   Thread.setDefaultUncaughtExceptionHandler(exceptionHandler)
   throw RuntimeException("Whoops!")
}

override fun uncaughtException(thread: Thread, exc: Throwable) {
   val report = Report(exc)

In the example above, our [CODE]UncaughtExceptionHandler[/CODE] will now generate a [CODE]Report[/CODE] object that contains a stacktrace for each unhandled error. We'll also call [CODE]Thread.getAllStackTraces()[/CODE] to obtain stacktraces for all running threads in our application, which can be immensely useful for tracking down those tricky concurrency bugs.

Finally, we'll add a field of type [CODE]Foo[/CODE], to demonstrate that we can capture arbitrary information about the application at this point.

Delivering an error report on the JVM

After generating a basic error report, the next step in a crash reporting SDK would be to serialise the report to JSON. If all goes well, we'll then make a request to an error reporting API, so that we can quickly be altered that our app is crashing in production. We'll achieve this by adding a [CODE]Delivery[/CODE] interface that delivers a Report to an arbitrary location:

-- CODE language-kotlin --
override fun uncaughtException(thread: Thread, exc: Throwable) {
   val report = Report(exc)
   delivery.deliver(report)
}

interface Delivery {
   fun deliver(report: Report)
}

There are a surprising amount of error conditions that we need to account for within the [CODE]Delivery[/CODE], such as caching reports locally when there's no network connectivity, and ensuring the handler doesn't make long-lived requests, which can be killed by later versions of the OS. We'll cover this in more depth in our next post.

Would you like to know more?

Hopefully this has helped you learn a bit more about Error Handling on Android. If you have any questions or feedback, please feel free to get in touch.

———

Try Bugsnag's Android crash reporting.

Bugsnag helps you prioritize and fix software bugs while improving your application stability
Request a demo