I am working on an Android Application which is integrated with MixPanel for analytics and BugSnag for error monitoring.
Recently we found crash in the application and as we couldn't find the root cause of the crash we added code to restart the app when the bug occurs. Along with restart, we also started tracking how many times the bug occurs. My preference was to use Bugsnag for the same, but couple of people in the team asked why can't we use MixPanel because we can easily filter out the events with parameters which we sent to MixPanel. But I feel MixPanel shouldn't be used as its specifically for tracking user events. And neither the crash nor the restart happens because of a user event, it just happens randomly.
I would like to hear suggestions/thoughts from the community regarding the same.
You could use Thread.setDefaultUncaughtExceptionHandler(...) in your Application.onCreate to set your custom Thread.UncaughtExceptionHandler that tracks to MixPanel all the UncaughtExceptions (Crashes) and set properties such as :
public class MyExceptionHandler implements UncaughtExceptionHandler
{
private UncaughtExceptionHandler defaultExceptionHandler;
public MyExceptionHandler (UncaughtExceptionHandler defaultExceptionHandler)
{
this.defaultExceptionHandler = defaultExceptionHandler;
}
public void uncaughtException(Thread thread, Throwable exception)
{
mMixPanelInstance.trackEvent("APP_CRASH", null);
if (defaultExceptionHandler != null)
{
defaultExceptionHandler.uncaughtException(thread, exception);
}
}
}
MyApplication.onCreate(...)
{
UncaughtExceptionHandler currentHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(currentHandler));
}
Related
Background
We use Crashlytics SDK to manage app crashes and get needed information about them.
So far, the information that the SDK automatically gathered was enough
The problem
I'd like to add more information for each crash, such as: available&total heap memory, activity stack,...
Thing is, I don't see a way to achieve this.
I know that the way Android framework works with unhandled exceptions is pretty easy (using Thread.setDefaultUncaughtExceptionHandler) and it's probably how the SDK works, but I can't find where to use the listener of the SDK itself.
What I've tried
The SDK has a listener, but it seems it's not of the current session, as shown here. The function name is "crashlyticsDidDetectCrashDuringPreviousExecution" , meaning it's of the previous session. Same callback was available before in deprecated methods.
There are "Custom Logging" and "Custom Keys" features, but those occur when I call them (not right when the crash occurs).
The question
Is there a way to add extra information to Crashlytics right when a crash occurs ?
If so, how?
Try creating an UncaughtExceptionHandler and use Custom Key(s) to store the information you want to be associated with your crash report.
Create your custom UncaughtExceptionHandler (ensuring that it will pass exception to default UncaughtExceptionHandler to be handled later via Crashlytics).
In the uncaughtException method add custom logic to set your key e.g. Crashlytics.setString("available_memory", "5784");
Check your Crashlytics dashboard to view your custom key(s) when your app crashes
Create a custom Application subclass to hold your logic:
public class MyApplication extends Application {
private static Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
private static Thread.UncaughtExceptionHandler mCaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// Custom logic goes here
// Calculate available memory
Crashlytics.setString("available_memory", "5784");
// This will make Crashlytics do its job
mDefaultUncaughtExceptionHandler.uncaughtException(thread, ex);
}
};
#Override
public void onCreate() {
super.onCreate();
// Order is important!
// First, start Crashlytics
Crashlytics.start(this);
// Second, cache a reference to default uncaught exception handler
mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
// Third, set custom UncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionHandler(mCaughtExceptionHandler);
}
}
Remember to specify the name of your Application subclass in your AndroidManifest.xml’s tag
<application android:name="MyApplication">
I tried to send a custom data with acra(and without crashing my app) with these 2 lines
ACRA.getErrorReporter().putCustomData("myKey", "myValue");
ACRA.getErrorReporter().handleException(null);
But in the report that I received was just:
Report requested by developer
The problem is that in my report there was no "myKey" or "myValue". How can I fix that? Maybe the problem is that I didn't include some specific Report Fields?
After that I tried with just
ACRA.getErrorReporter().handleException(null);
and it worked as with 2 lines above??
ACRA is designed to capture unhandled exceptions. It also provides functionality to silently send handled exceptions. Sending null is way outside its design parameters.
Try sending an actual exception.
I did a little workaround to group specific messages sent by it's exception. I introduced "FakeException" and removed stack trace so the message would be smaller.
public class FakeException extends Exception {
#Override
public Throwable fillInStackTrace() {
return null;
}
#Override
public void setStackTrace(StackTraceElement[] trace) {
super.setStackTrace(null);
}
}
Now You can create another Exception that extends FakeException and do something like this:
public void SendUserReport(String reportText){
ACRA.getErrorReporter().putCustomData(USER_REPORT_CONTENT_TAG, reportText);
ACRA.getErrorReporter().handleSilentException(new NotificationFromUserFakeException());
ACRA.getErrorReporter().removeCustomData(USER_REPORT_CONTENT_TAG);
}
The following picture shows this report in Acralyzer (awesome software, definitely use it).
I am working on an Android library where I need to perform some action if an uncaught exception is seen.
I am successfully setting my uncaught exception handler and running the code I need, which is to post some information to a web server, but after my part is finished I want the app to then do what Android usually does, i.e. display a dialogue to the informing them that it has crashed and then exit the app, and post the details to the Google Play Developer Console.
At the moment, my uncaught exception successfully posts to the server, but then keeps the app running but in a bit of a weird state as the thread has party disappeared, where usually, if my an uncaught exception is thrown, then Android closes the app.
Below is how I am doing my uncaught exception handler:
private static void setUnhandledExceptionHandler()
{
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler()
{
#Override
public void uncaughtException(final Thread thread, final Throwable) {
CrashReporter.ReportUnhandledCrash(((Exception) ex));
Looper.loop();
}
});
}
Basically, what I want to do is, have my app post to to my server via my unhandled exception handler and then quit the app, in the same way that Android usually does, i.e. display a force close error the use and close the app.
I had a similar problem when tracking to GoogleAnalytics - most Exceptions got lost when i tried to report them in the default handler. So i cached them and reported them on the next startup.
The second trick (and this hopefully answers your question) to let the Application crash as it should is to store the 'old' DefaultUncaughtExceptionHandler and pass the exception to it.
The onCreate method below is the one of my Application class
#Override
public void onCreate()
{
super.onCreate();
trackCachedException();
setupExceptionHandling();
}
private void setupExceptionHandling()
{
_systemExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.UncaughtExceptionHandler myHandler = new Thread.UncaughtExceptionHandler()
{
#Override
public void uncaughtException(Thread thread, Throwable ex)
{
cacheException(thread.getName(), ex);
if (_systemExceptionHandler != null)
{
_systemExceptionHandler.uncaughtException(thread, ex);
}
}
};
// Make myHandler the new default uncaught exception handler.
Thread.setDefaultUncaughtExceptionHandler(myHandler);
}
The only way I can see how you can do this is neither clean nor pleasant, so I'll say sorry before we begin...
The order of operations is:
Store the uncaught exception
Log the exception as you have done previously
Deregister your uncaught exception handler, by calling the set method with null.
Launch a new thread that throws your stored exception.
You are effectively throwing the same exception twice, but removing the recovery mechanism on the second throw.
I just released a new version of my application to the Android market, and my new version has a GLSurfaceView in the activity. Even though I'm not doing anything fancy, I have a large user base, there's a lot of substandard Android phones out there, and I'm invariably getting exceptions in GLThread.run().
What is the recommended way to catch/handle these exceptions without crashing the entire app? Ideally I'd like to be able to catch the error, remove the surface view from the activity and switch off the component that uses OpenGL. I did a bit of searching but mostly found exception reports for Firefox on Android and stuff like that. :)
I'm thinking of just using an uncaught exception handler, switching a shared preferences flag to false, and letting it crash; the next run I won't try to add that GLSurfaceView.
I ended up working around the problem with the following code:
final UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
if (thread.getName().startsWith("GLThread")) {
disableOpenGLStuff();
}
// You could wrap this in an else, but I'm not sure how good of an idea it is to leave the application running when a thread has crashed.
defaultHandler.uncaughtException(thread, ex);
});
In my Android application I utilize setDefaultUncaughtExceptionHandler to store information about unhandled exceptions locally on a user device. After some feedback I suspect that this code prevents the built-in Google's error-reporting feature from work, because I do not see error reports in the developer console, while exceptions are reported by users. Their devices are well past 2.2, where the error-reporting was introduced. Could it be that specific device with, say, 4.0.3 does not support this feature? If yes, how can I detect this programmatically?
I can't find information regarding this in Android documentation. I'd like both standard error-reporting and my custom handling work together. In my custom exception handler I call Thread.getDefaultUncaughtExceptionHandler() to get default handler, and in my implementation of uncaughtException I propagate exception to this default handler as well.
I first tried calling System.exit(1); as mentioned in this SO answer, but that didn't work.
Finally solved it by calling the uncaughtException(Thread thread, Throwable ex) again on Androids default UncaughtExceptionHandler (found it by checking the ACRA source code.
Example Activity
public class MainActivity extends Activity implements Thread.UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler _androidUncaughtExceptionHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_androidUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
// Rest onCreate
setContentView(R.layout.main_activity);
}
//#Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
// Do your stuff with the exception
} catch (Exception e) {
/* Ignore */
} finally {
// Let Android show the default error dialog
_androidUncaughtExceptionHandler.uncaughtException(thread, ex);
}
}
}
Yes, this will stop the inbuilt error report. The user is given a dialog when your app crashes, with an option to report the error via Google Play. However, if you use setDefaultUncaughtExceptionHandler() then the exception is handled within your app, and no option is given to report it.
I recommend that you integrate ACRA into your project, as it allows you to easily receive error reports upon crashes.