Android catch all exceptions

Error handling on Android part 1: how exceptions work for JVM and Android apps

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?

This is the first 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 exceptions work for JVM and Android apps?

Exceptions should be thrown in exceptional circumstances where the calling code needs to decide how to recover from an error condition.

What is an Exception object?

A [CODE]Throwable[/CODE] is a special type of object which can be thrown and alter the execution path of a JVM application. For example, the code snippet below throws an [CODE]IllegalStateException[/CODE]:

— CODE language-kotlin —
fun main() <
В В В try <
В В В В В В В throw IllegalStateException()
В В В В В В В println(«Hello World»)
В В В > catch (exc: Throwable) <
В В В В В В В println(«Something went wrong»)
В В В >
>

Throwing an exception means that the execution flow changes, and ‘Hello World’ is never printed. Instead, the program counter will jump to the nearest catch block, and executes the error recovery code within, meaning our program prints ‘Something went wrong’ instead.

Of course, it doesn’t always make sense to recover from a failure — for example, if an [CODE]OutOfMemoryError[/CODE] is thrown by the JVM, there is very little prospect of ever recovering from this condition. In this case it makes sense to leave the [CODE]Throwable as unhandled[/CODE], and allow the process to terminate so the user can restart the app from a fresh state.

Anatomy of the Throwable class

[CODE]Throwable[/CODE] has two direct subclasses: [CODE]Exception[/CODE], and [CODE]Error[/CODE]. Typically an [CODE]Error[/CODE] is thrown in conditions where recovery is not possible, and an [CODE]Exception[/CODE] where recovery is possible. Additionally, there are many subclasses of [CODE]Exception[/CODE] which convey additional meaning — for example, an [CODE]IllegalArgumentException[/CODE] would indicate the programmer passed an invalid argument, and an [CODE]IllegalStateException[/CODE] would indicate that the program encountered an unanticipated state.

— CODE language-kotlin —
fun main() <
В В В try <
В В В В В В В throw IllegalStateException(«This should never happen!»)
В В В В В В В println(«Hello World»)
В В В > catch (exc: Throwable) <
В В В В В В В println(«Something went wrong»)
В В В >
>

Let’s consider the above snippet again. The constructed [CODE]IllegalStateException[/CODE] object captures a snapshot of the application at the time of the error condition:

— CODE language-kotlin —
java.lang.IllegalStateException: This should never happen!
at com.example.myapplication.Exceptions101Kt.foo(Exceptions101.kt:12)
at com.example.myapplication.Exceptions101Kt.main(Exceptions101.kt:5)
at com.example.myapplication.Exceptions101Kt.main(Exceptions101.kt)

This is commonly called a stacktrace. Each line represents a single frame in the application’s call stack at the time of the error, which match the filename, method name, and line number of our original code snippet.

A stacktrace can also contain other useful information, such as program state, which in this case is a static error message, but we could equally pass in arbitrary variables.

Exception handling hierarchy

After throwing an exception, an exception handler must be found to handle the exception, or the app will terminate. In the JVM, this is a well-defined hierarchy, which we’ll run through here.

First up in the exception handling hierarchy is a catch block:

— CODE language-kotlin —
try <
В В В crashyCode()
> catch (exc: IllegalStateException) <
В В В // handle throwables of type IllegalStateException
>

If a catch block isn’t available in the current stack frame, but is defined further down the call stack, then the exception will be handled there.

Next in the hierarchy is implementations of [CODE]UncaughtExceptionHandler[/CODE]. This interface contains a single method which is invoked whenever a [CODE]Throwable[/CODE] is thrown, after the handler has been set:

— CODE language-kotlin —
val currentThread = Thread.currentThread()
currentThread.setUncaughtExceptionHandler < thread, exc ->
В В В // handle all uncaught JVM exceptions in the current Thread
>

It’s possible to set an [CODE]UncaughtExceptionHandler[/CODE] in a few different places; the JVM has a defined hierarchy for these. First, if a handler has been set on the current [CODE]Thread[/CODE], this will be invoked. Next up will be a handler on the [CODE]ThreadGroup[/CODE], before finally, the default handler is invoked, which will handle all uncaught JVM exceptions by printing a stacktrace, and then terminating the app.

— CODE language-kotlin —
Thread.setDefaultUncaughtExceptionHandler < thread, exc ->
В В В // handle all uncaught JVM exceptions
>

It’s the default [CODE]UncaughtExceptionHandler[/CODE] that is most interesting from an error reporting point-of-view, and it’s the default [CODE]UncaughtExceptionHandler[/CODE] that is responsible for showing that all too familiar crash dialog on Android.

Читайте также:  Андроид режим без кнопок

The [CODE]UncaughtExceptionHandler[/CODE] interface is the building block of all crash reporting SDKs on the JVM, such as bugsnag-android or bugsnag-java. Read on in part two to learn how we can define a custom handler for uncaught exceptions, and use it to create a crash reporting SDK.

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.

Источник

Catch all possible android exception globally and reload application

I know the best way to prevent system crashes is catching all possible exception in different methods. So I use try catch blocks every where in my code. However as you know sometimes you forget to test some scenarios which cause some unhanded exceptions and a user gets «Unfortunately App stopped working. » message. This is bad for any application. Unfortunately the people who will use my app are not native English, so they will not understand crash message too.

So I want to know is it possible to catch all possible exception globally ( with just one try catch block in some main classes not all classes and methods. ) and reload application automatically and without any weird messages? Or at least is it possible to change the crash message?

3 Answers 3

In your onCreate

So I want to know is it possible to catch all possible exception globally . and reload application automatically

DO NOT DO THIS. If your app enters a state where even just launching it causes a crash, you will create an endless loop of crashing and relaunching that the user cannot get out of.

The error message is not weird; it is the system message, and it is translated in all supported languages on the device.

The only thing you should be doing is using some kind of crash reporting library to report crashes back to you without the user having to do anything, so that you can fix your app and submit an update. You can set a custom UncaughtExceptionHandler in your Application’s onCreate() , but I would limit this to logging data and perhaps preparing to send that to you for debugging purposes, and then forward the call back to the default UncaughtExceptionHandler . This is exactly what a crash reporting library does.

I know the best way to prevent system crashes is catching all possible exception in different methods. So I use try catch blocks every where in my code.

No, the best way to to write good code and fix bugs before releasing. It is considered bad practice to catch all forms of exceptions indiscriminately. You should be using try-catch blocks only where either

  1. A method call can throw a checked exception, in which case the compiler forces you to surround with try-catch or rethrow the exception; or
  2. You want to handle certain unchecked (runtime) exceptions. An example would be parsing user input with something like Integer.parseInt() , catching the NumberFormatException and showing message to the user that their input is invalid.

However as you know sometimes you forget to test some scenarios

Then improve your coding and your testing practices. Do not use this as an excuse for taking rash actions.

Unfortunately the people who will use my app are not native English, so they will not understand crash message too.

Источник

Using Global Exception Handling on android

Is there a code example, or a tutorial on how to use the Thread.setDefaultUncaughtExceptionHandler method? Basically I’m trying to display a custom alert dialog, whenever an exception is thrown, in my application. Is it possible to do this? I know it’s a little bit tricky to display something on the screen, if the exception is thrown in the UI thread but I don’t know any work around for this.

6 Answers 6

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

Class for handling error:

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

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

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:

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:

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()

Источник

Exceptions in coroutines

Cancellation and Exceptions in coroutines (Part 3) — Gotta catch ’em all!

We, developers, usually spend a lot of time polishing the happy path of our app. However, it’s equally important to provide a proper user experience whenever things don’t go as expected. On one hand, seeing an application crash is a bad experience for the user; on the other hand, showing the right message to the user when an action didn’t succeed is indispensable.

Handling exceptions properly has a huge impact on how users perceive your application. In this article, we’ll explain how exceptions are propagated in coroutines and how you can always be in control, including the different ways to handle them.

If you prefer video, check out this talk from KotlinConf’19 by Florina Muntenescu and I:

⚠️ In order to follow the rest of the article without any problems, reading and understanding Part 1 of the series is required.

Coroutines: First things first

Cancellation and Exceptions in Coroutines (Part 1)

A coroutine suddenly failed! What now? 😱

When a coroutine fails with an exception, it will propagate said exception up to its parent! Then, the parent will 1) cancel the rest of its children, 2) cancel itself and 3) propagate the exception up to its parent.

The exception will reach the root of the hierarchy and all the coroutines that the CoroutineScope started will get cancelled too.

While propagating an exception can make sense in some cases, there are other cases when that’s undesirable. Imagine a UI-related CoroutineScope that processes user interactions. If a child coroutine throws an exception, the UI scope will be cancelled and the whole UI component will become unresponsive as a cancelled scope cannot start more coroutines.

What if you don’t want that behavior? Alternatively, you can use a different implementation of Job , namely SupervisorJob , in the CoroutineContext of the CoroutineScope that creates these coroutines.

SupervisorJob to the rescue

With a SupervisorJob , the failure of a child doesn’t affect other children. A SupervisorJob won’t cancel itself or the rest of its children. Moreover, SupervisorJob won’t propagate the exception either, and will let the child coroutine handle it.

You can create a CoroutineScope like this val uiScope = CoroutineScope(SupervisorJob()) to not propagate cancellation when a coroutine fails as this image depicts:

If the exception is not handled and the CoroutineContext doesn’t have a CoroutineExceptionHandler (as we’ll see later), it will reach the default thread’s ExceptionHandler . In the JVM, the exception will be logged to console; and in Android, it will make your app crash regardless of the Dispatcher this happens on.

💥 Uncaught exceptions will always be thrown regardless of the kind of Job you use

The same behavior applies to the scope builders coroutineScope and supervisorScope . These will create a sub-scope (with a Job or a SupervisorJob accordingly as a parent) with which you can logically group coroutines (e.g. if you want to do parallel computations or you want them to be or not be affected by each other).

Warning: A SupervisorJob only works as described when it’s part of a scope: either created using supervisorScope or CoroutineScope(SupervisorJob()) .

Job or SupervisorJob? 🤔

When should you use a Job or a SupervisorJob ? Use a SupervisorJob or supervisorScope when you don’t want a failure to cancel the parent and siblings.

In this case, if child#1 fails, neither scope nor child#2 will be cancelled.

In this case, as supervisorScope creates a sub-scope with a SupervisorJob , if child#1 fails, child#2 will not be cancelled. If instead you use a coroutineScope in the implementation, the failure will get propagated and will end up cancelling scope too.

Читайте также:  Пульт управления по блютузу для андроид

Watch out quiz! Who’s my parent? 🎯

Given the following snippet of code, can you identify what kind of Job child#1 has as a parent?

child#1 ’s parentJob is of type Job ! Hope you got it right! Even though at first impression, you might’ve thought that it can be a SupervisorJob , it is not because a new coroutine always gets assigned a new Job() which in this case overrides the SupervisorJob . SupervisorJob is the parent of the coroutine created with scope.launch ; so literally, SupervisorJob does nothing in that code!

Therefore, if either child#1 or child#2 fails, the failure will reach scope and all work started by that scope will be cancelled.

Remember that a SupervisorJob only works as described when it’s part of a scope: either created using supervisorScope or CoroutineScope(SupervisorJob()) . Passing a SupervisorJob as a parameter of a coroutine builder will not have the desired effect you would’ve thought for cancellation.

Regarding exceptions, if any child throws an exception, that SupervisorJob won’t propagate the exception up in the hierarchy and will let its coroutine handle it.

Under the hood

If you’re curious about how Job works under the hood, check out the implementation of the functions childCancelled and notif y Cancelling in the JobSupport.kt file.

In the SupervisorJob implementation, the childCancelled method just returns false , meaning that it doesn’t propagate cancellation but it doesn’t handle the exception either.

Dealing with Exceptions 👩‍🚒

Coroutines use the regular Kotlin syntax for handling exceptions: try/catch or built-in helper functions like runCatching (which uses try/catch internally).

We said before that uncaught exceptions will always be thrown. However, different coroutines builders treat exceptions in different ways.

Launch

With launch, exceptions will be thrown as soon as they happen. Therefore, you can wrap the code that can throw exceptions inside a try/catch , like in this example:

With launch, exceptions will be thrown as soon as they happen

Async

When async is used as a root coroutine (coroutines that are a direct child of a CoroutineScope instance or supervisorScope ), exceptions are not thrown automatically, instead, they’re thrown when you call .await() .

To handle exceptions thrown in async whenever it’s a root coroutine, you can wrap the .await() call inside a try/catch :

In this case, notice that calling async will never throw the exception, that’s why it’s not necessary to wrap it as well. await will throw the exception that happened inside the async coroutine.

When async is used as a root coroutine, exceptions are thrown when you call .await

Also, notice that we’re using a supervisorScope to call async and await . As we said before, a SupervisorJob lets the coroutine handle the exception; as opposed to Job that will automatically propagate it up in the hierarchy so the catch block won’t be called:

Furthermore, exceptions that happen in coroutines created by other coroutines will always be propagated regardless of the coroutine builder. For example:

In this case, if async throws an exception, it will get thrown as soon as it happens because the coroutine that is the direct child of the scope is launch . The reason is that async (with a Job in its CoroutineContext ) will automatically propagate the exception up to its parent ( launch ) that will throw the exception.

⚠️ Exceptions thrown in a coroutineScope builder or in coroutines created by other coroutines won’t be caught in a try/catch!

In the SupervisorJob section, we mention the existence of CoroutineExceptionHandler . Let’s dive into it!

CoroutineExceptionHandler

The CoroutineExceptionHandler is an optional element of a CoroutineContext allowing you to handle uncaught exceptions.

Here’s how you can define a CoroutineExceptionHandler , whenever an exception is caught, you have information about the CoroutineContext where the exception happened and the exception itself:

Exceptions will be caught if these requirements are met:

  • When ⏰: The exception is thrown by a coroutine that automatically throws exceptions (works with launch , not with async ).
  • Where 🌍: If it’s in the CoroutineContext of a CoroutineScope or a root coroutine (direct child of CoroutineScope or a supervisorScope ).

Let’s see some examples using the CoroutineExceptionHandler defined above. In the following example, the exception will be caught by the handler:

In this other case in which the handler is installed in a inner coroutine, it won’t be caught:

The exception isn’t caught because the handler is not installed in the right CoroutineContext . The inner launch will propagate the exception up to the parent as soon as it happens, since the parent doesn’t know anything about the handler, the exception will be thrown.

Dealing with exceptions gracefully in your application is important to have a good user experience, even when things don’t go as expected.

Remember to use SupervisorJob when you want to avoid propagating cancellation when an exception happens, and Job otherwise.

Uncaught exceptions will be propagated, catch them to provide a great UX!

Источник

Оцените статью