Is there a way to catch, in one place, all "uncaught" exceptions and let the app continue?
I see I can use setUncaughtExceptionHandler to do some logging and whatever, but the app will still terminate. I want something where I can log an exception, tell the user his action failed, and let him keep going (try something else).
Thanks.
No. I'm not sure why you'd want it. Catch Exceptions from methods that are known to throw them, and test your code to avoid Exceptions such as NullPointerException. That's the way to write good code.
Sorry to revisit such an old thread but since there's no proper answer to it I felt obliged to provide a solution.
Add this to the activity that opens every time the app is used (MainActivity for example) -
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
Log.e("Exception"+Thread.currentThread().getStackTrace()[2],paramThrowable.getLocalizedMessage());
}
});
What it does is that it sets a default handler for uncaught exceptions and doesn't quit the app when one pops up. However, with great power comes great responsibility. I don't recommend you use this as a way to escape the responsibility of catching exceptions in your code (by using a try-catch for example). Test your code to make it as bug-free as possible before launch.
Related
I'm currently working on a pretty big music app. And for some reason, my app randomly closes, without any error message.
I catch uncaught exceptions in the project using the following code:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {...});
but even if it works every time for obvious exceptions, I don't catch any error when the app closes by itself. Also, it's not device related, since I tried on 10 differents devices of different brands with different versions of Android.
My question is: has anyone experienced something similar and if yes, what was the solution of your problem? Or, in other words: is there any case where an Android app can close by itself without any error (and without using System.exit())
I know it's not a very specific question, but since I have no idea where it comes from, I can't do any better so far.
The default uncaught handler yo are overriding are the responsible to display the error to the user, the solution is to have a reference to the old one and call it on yours as follow:
final Thread.UncaughtExceptionHandler appDefault = Thread.GetDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
// DO STUFF BEFORE APP CRASHES
appDefault.uncaughtException(t, e); // call later, so the app crashes as default
}});
My Android app currently uses a custom UncaughtExceptionHandler that aims to capture any crash, and schedules an app restart for several seconds in the future with AlarmManager before manually calling Process.killProcess(Process.myPid()) to avoid Android's Force Close popup as in my app's use case, the user will not be able to interact with the device to tap "ok" on the FC dialog and restart the app.
Now, I'd like to integrate with Firebase Crash reports, but I fear wrong behaviors, so here are my questions:
How should I make my code so my custom UncaughtExceptionHandler passes the exception to Firebase Crash Report before killing it's process? Would calling Thread.getDefaultUncaughtExceptionHandler() give me the Firebase Crash report UncaughtExceptionHandler so I can just call uncaughtException(...) on it?
May Process.killProcess(Process.myPid()) prevent Firebase Crash reporting library to do it's reporting work? Would Firebase have started it's crash report in a separated process before it's uncaughtException(...) returns? Does Firebase own UncaughtExceptionHandler calls back to Android default's UncaughtExceptionHandler, showing the FC dialog?
May Process.killProcess(Process.myPid()) kill Firebase Crash Reporting process in addition to the default process?
How can my custom Application class detect if it is instantiated in Firebase Crash Reporting process? Treating both processes the same way would probably lead to inconsistent states.
Thanks to anyone that tries to help me!
If you kill the process in your exception handler, you will not be able to receive crashes. It will interfere with the ability to persist the crash for either immediate or delayed transmission. It will possibly interfere with any library that has registered uncaught exception handlers that behave well.
In fact, Process.killProcess(Process.myPid()) is very much an anti-pattern for Android development. Android apps should not be concerned at all with the overall lifecycle if the process that hosts the app. The fact that Android manages the process for you is an optimization designed for the benefit of the users.
I strongly recommend, for uncaught exceptions in your app, to simply let the app die as it normally would. Masking the proper effect of the crash is like sweeping dirt under a rug. You might be resolving a short term problem, but what really needs to happen is the normal logging and handling of the error so you can fix it.
Don't depend on the fact that Firebase Crash Reporting transmits exceptions in another process. That other process will be removed in the full non-beta release.
The best situation for your Application subclass is to not depend at all which process it's operating. In fact, the Android team at Google does not recommend use of Application subclasses at all since it only leads to trouble for multi-process apps. If you must use an Application subclass, it should expect to run within multiple processes.
After some testing, I finally found a way to both ensure my app restarts properly after an UncaughtException.
I attempted three different approaches, but only the first, which is my original code, with just a little tweak to pass the uncaught Throwable to `FirebaseCrash, and ensure it is considered as a FATAL error.
The code that works:
final UncaughtExceptionHandler crashShield = new UncaughtExceptionHandler() {
private static final int RESTART_APP_REQUEST = 2;
#Override
public void uncaughtException(Thread thread, Throwable ex) {
if (BuildConfig.DEBUG) ex.printStackTrace();
reportFatalCrash(ex);
restartApp(MyApp.this, 5000L);
}
private void reportFatalCrash(Throwable exception) {
FirebaseApp firebaseApp = FirebaseApp.getInstance();
if (firebaseApp != null) {
try {
FirebaseCrash.getInstance(firebaseApp)
.zzg(exception); // Reports the exception as fatal.
} catch (com.google.firebase.crash.internal.zzb zzb) {
Timber.wtf(zzb, "Internal firebase crash reporting error");
} catch (Throwable t) {
Timber.wtf(t, "Unknown error during firebase crash reporting");
}
} else Timber.wtf("no FirebaseApp!!");
}
/**
* Schedules an app restart with {#link AlarmManager} and exit the process.
* #param restartDelay in milliseconds. Min 3s to let the user got in settings force
* close the app manually if needed.
*/
private void restartApp(Context context, #IntRange(from = 3000) long restartDelay) {
Intent restartReceiver = new Intent(context, StartReceiver_.class)
.setAction(StartReceiver.ACTION_RESTART_AFTER_CRASH);
PendingIntent restartApp = PendingIntent.getBroadcast(
context,
RESTART_APP_REQUEST,
restartReceiver,
PendingIntent.FLAG_ONE_SHOT
);
final long now = SystemClock.elapsedRealtime();
// Line below schedules an app restart 5s from now.
mAlarmManager.set(ELAPSED_REALTIME_WAKEUP, now + restartDelay, restartApp);
Timber.i("just requested app restart, killing process");
System.exit(2);
}
};
Thread.setDefaultUncaughtExceptionHandler(crashShield);
Explanation of why and unsuccessful attempts
It's weird that the hypothetically named reportFatal(Throwable ex) method from FirebaseCrash class has it's name proguarded while being still (and thankfully) public, giving it the following signature: zzg(Throwable ex).
This method should stay public, but not being obfuscated IMHO.
To ensure my app works properly with multi-process introduced by Firebase Crash Report library, I had to move code away from the application class (which was a great thing) and put it in lazily loaded singletons instead, following Doug Stevenson's advice, and it is now multi-process ready.
You can see that nowhere in my code, I called/delegated to the default UncaughtExceptionHandler, which would be Firebase Crash Reporting one here. I didn't do so because it always calls the default one, which is Android's one, which has the following issue:
All code written after the line where I pass the exception to Android's default UncaughtExceptionHandler will never be executed, because the call is blocking, and process termination is the only thing that can happen after, except already running threads.
The only way to let the app die and restart is by killing the process programmatically with System.exit(int whatever) or Process.kill(Process.myPid()) after having scheduled a restart with AlarmManager in the very near future.
Given this, I started a new Thread before calling the default UncaughtExceptionHandler, which would kill the running process after Firebase Crash Reporting library would have got the exception but before the scheduled restart fires (requires magic numbers). It worked on the first time, removing the Force Close dialog when the background thread killed the process, and then, the AlarmManager waked up my app, letting it know that it crashed and has a chance to restart.
The problem is that the second time didn't worked for some obscure and absolutely undocumented reasons. The app would never restart even though the code that schedules a restart calling the AlarmManager was properly run.
Also, the Force Close popup would never show up. After seeing that whether Firebase Crash reporting was included (thus automatically enabled) or not didn't change anything about this behavior, it was tied to Android (I tested on a Kyocera KC-S701 running Android 4.4.2).
So I finally searched what Firebase own UncaughtExceptionHandler called to report the throwable and saw that I could call the code myself and manage myself how my app behaves on an uncaught Throwable.
How Firebase could improve such scenarios
Making the hypothetically named reportFatal(Throwable ex) method non name-obfuscated and documented, or letting us decide what happens after Firebase catches the Throwable in it's UncaughtExceptionHandler instead of delegating inflexibly to the dumb Android's default UncaughtExceptionHandler would help a lot.
It would allow developers which develop critical apps that run on Android to ensure their app keep running if the user is not able to it (think about medical monitoring apps, monitoring apps, etc).
It would also allow developers to launch a custom activity to ask users to explain how it occurred, with the ability to post screenshots, etc.
BTW, my app is meant to monitor humans well-being in critical situations, hence cannot tolerate to stop running. All exceptions must be recovered to ensure user safety.
I'm building an app that sometimes crashes, I want to know that it crashed in the next time I opened it so I can suggest to the user some post-crash options.
How can I detect the crash?
Also I want to be able to save the user's work before it crashes, I means real time detection of crash, can I do it without knowing where it crashed?
You will need to know where it crashed in order to set the try/catch blocks in the right place to, er, catch the crash and save the data or whatever you have in mind.
This is known as "graceful" termination if you want to consider it in more detail.
Unfortunately neither Java destructor/finalize methods nor lifecycle methods such as onDestroy are anywhere near as robust as try/catch blocks so I'm afraid that is your only option, and how many of us deal with exception prevention. No-one would wittingly provide a user experience that crashes, much less with loss of their data.
Take a took at the ACRA library. You can configure it so whenever a crash happens you can control it and even send the crash log by email
You can use try/catch blocks, then send details on the Exception in your catch.
There are implement UncaughtExceptionHandler as mentioned in these answers and write crash report in some file or use it another way.
ACRA is already mentioned.
However for paid version, I found BugSnag is very good at this.
Or if you want to take the extra mile, try AppSee.
AppSee even has video recording session of how the crash happens. It is from tapping that button on the second list, the menu button or even when the user slides left in your viewpager.
I would like to be able to determine, in case an exception occurs while the user is using my application, where exactly the exception took place. I'd like to do something similar ti printStackTrace() method. (So this is during build mode, not debug mode )
Currently I've put almost all my methods from all my classes inside a try-catch statement (each method has a try-catch statement which encompasses all it's instructions) and i can, at this point, display the "tree" or stack of methods if an exception occurs. But is there a way to determine either a line number of something to more precisely indicate where inside the method the exception occurred? Similar to what is displayed when you use printStackTrace().
I'm not really used with Exception handling, what is the best practice for doing this and can in be done?
EDIT
And one other thing. When i use printStackTrace() during build mode, where does it display the content, because Logcat isn't available? Can i retrieve that information and maybe do something with it?
OR
Even better, can i use getStackTrace() during build mode and convert the stuff there in String and maybe output it somewhere?
All the exceptions that are not handled by your code and make your app crash in release mode will appear in the android developper console, close to your app.
For this to work, you will need to retrace obfuscated stack traces.
About exception handling : I suggest you read this for instance. You are making a mistake about exception handling if you surround all your code by a try/catch block.
Exception handling is more subtile than that and is often influenced by design considerations (whether to treat exceptions locally or throw them back to the caller).
To sum up : in the core of your app : don't treat exception but throw them or let them be thrown, using the throws clause of your methods signatures. In the upper layers, closer to the UI, treat exceptions with try/catch and if an error occurs, make sure your app is in a stable state and display some usefull messages to users.
More details (but not that much) :
in the database layer : throw exception. You can still catch them to log them, but throw or rethrow them to tell caller that something went wrong.
in the business layer : catch them, make sure your business/domain model is in a stable state and recovers from the error, and throw them back to the caller.
in the UI layer : catch the exceptions and display some messages to users.
I have an app that is running pretty stably (no more crashes actually), but as everybody knows your program crashes as soon as it gets in the hands of somebody else :D
What I would like is to find a(all) the place(s) where I can put a try{}catch(){} to be able catch and control what happens when the app crashes unexpectedly (display a better message, send log, possible recovery...)
I know its surely not that simple but still it would be good if there was a way to catch most of them.
(for example there is a small bug in GLSurfaceView that when it is being closed causes sometimes to crash because of an EGL swap buffer)
any ideas?
You should take a look at this article: http://stuffthathappens.com/blog/2007/10/07/programmers-notebook-uncaught-exception-handlers/
But be careful when using this, you might mask errors in your application and if you resort to this to just pretend your app is working, you're doing it wrong :)
Here's a really lazy way to catch any given exception:
try {
//do some stuff here
} catch (Throwable e) {
//handle exception here
}
This is useful if you have no idea what's going to be thrown. Consequently, it's not going to be very helpful for any kind of recovery. This is something I wouldn't use beyond the testing period of development.