Is it possible to incorporate custom UncaughtExceptionHandler along with crashlytics in one application? If yes - how?
UPDATE
Please see #kmityak answer as Crashlytics/Fabric initialization is now asynchronous and my solution below is no longer valid.
ORIGINAL ANSWER
You can set your custom UncaughtExceptionHandler providing that it will pass exception to default UncaughtExceptionHandler to be handled later via Crashlytics.
Below code is implemented inside Application subclass:
private static Thread.UncaughtExceptionHandler mDefaultUEH;
private static Thread.UncaughtExceptionHandler mCaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// Custom logic goes here
// This will make Crashlytics do its job
mDefaultUEH.uncaughtException(thread, ex);
}
};
#Override
public void onCreate() {
super.onCreate();
// Order is important!
// First, start Crashlytics
Crashlytics.start(this);
// Second, set custom UncaughtExceptionHandler
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(mCaughtExceptionHandler);
}
Second option is to register Crashlytics after setting your custom UncaughtExceptionHandler - then all uncaught exceptions will be reported by Crashlytics as fatals, and after that passed to your custom handler.
Since recent versions of Crashlytics perform initialization asynchronously, it's better to use Fabric's initialization callback:
private static Thread.UncaughtExceptionHandler mDefaultUEH;
private static Thread.UncaughtExceptionHandler mCaughtExceptionHandler =
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// Custom logic goes here
// This will make Crashlytics do its job
mDefaultUEH.uncaughtException(thread, ex);
}
};
CrashlyticsCore core = new CrashlyticsCore.Builder()
.disabled(BuildConfig.DEBUG)
.build();
Fabric.with(new Fabric.Builder(this).kits(new Crashlytics.Builder()
.core(core)
.build())
.initializationCallback(new InitializationCallback<Fabric>() {
#Override
public void success(Fabric fabric) {
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(mCaughtExceptionHandler);
}
#Override
public void failure(Exception e) {
}
})
.build());
Yes, it is possible.
In your Application class:
#Override
public void onCreate() {
super.onCreate();
Crashlytics.start(this);
initUncaughtExceptionHandler();
}
private void initUncaughtExceptionHandler() {
final ScheduledThreadPoolExecutor c = new ScheduledThreadPoolExecutor(1);
c.schedule(new Runnable() {
#Override
public void run() {
final UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
// do my amazing stuff here
System.err.println("Error!");
//then pass the job to the previous handler
defaultHandler.uncaughtException(paramThread, paramThrowable);
}
});
}
}, 5, TimeUnit.SECONDS);
}
The reason I'm scheduling this after 5 seconds is because Crashlytics needs some time to set up his stuff. I'm using this code and it works perfectly. Of course if your app crashes on start, sorry but no custom handler ;)
None if those solutions worked for me. I did it like following:
// Setup handler for uncaught exceptions.
Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
{
#Override
public void uncaughtException (Thread thread, Throwable e)
{
//Send Report to Crashlytics. Crashlytics will send it as soon as it starts to work
Crashlytics.logException(e);
//Your custom codes to Restart the app or handle this crash
HandleCrashes(thread, e);
}
});
And here is my Custom Method to restart the APP:
private void HandleCrashes(Thread t, Throwable e) {
Intent i = new Intent(mContext, frmLogin.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("JustLogin", true);
startActivity(i);
System.exit(1);
}
Turn off automatic collection
add this to your AndroidManifest.xml
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
Register your custome UncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionHandler(customerUncaughtExceptionHandler)
Manually start Crashlytics after registered UncaughtExceptionHandler
Fabric.with(this, new Crashlytics());
rebuild and reinstall your Application
I found a solution for Fabric 2.10.1:
Thread.setDefaultUncaughtExceptionHandler(yourExceptionHandler)
Fabric.with(this, Crashlytics())
Related
Is it possible to incorporate custom UncaughtExceptionHandler along with crashlytics in one application? If yes - how?
UPDATE
Please see #kmityak answer as Crashlytics/Fabric initialization is now asynchronous and my solution below is no longer valid.
ORIGINAL ANSWER
You can set your custom UncaughtExceptionHandler providing that it will pass exception to default UncaughtExceptionHandler to be handled later via Crashlytics.
Below code is implemented inside Application subclass:
private static Thread.UncaughtExceptionHandler mDefaultUEH;
private static Thread.UncaughtExceptionHandler mCaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// Custom logic goes here
// This will make Crashlytics do its job
mDefaultUEH.uncaughtException(thread, ex);
}
};
#Override
public void onCreate() {
super.onCreate();
// Order is important!
// First, start Crashlytics
Crashlytics.start(this);
// Second, set custom UncaughtExceptionHandler
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(mCaughtExceptionHandler);
}
Second option is to register Crashlytics after setting your custom UncaughtExceptionHandler - then all uncaught exceptions will be reported by Crashlytics as fatals, and after that passed to your custom handler.
Since recent versions of Crashlytics perform initialization asynchronously, it's better to use Fabric's initialization callback:
private static Thread.UncaughtExceptionHandler mDefaultUEH;
private static Thread.UncaughtExceptionHandler mCaughtExceptionHandler =
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// Custom logic goes here
// This will make Crashlytics do its job
mDefaultUEH.uncaughtException(thread, ex);
}
};
CrashlyticsCore core = new CrashlyticsCore.Builder()
.disabled(BuildConfig.DEBUG)
.build();
Fabric.with(new Fabric.Builder(this).kits(new Crashlytics.Builder()
.core(core)
.build())
.initializationCallback(new InitializationCallback<Fabric>() {
#Override
public void success(Fabric fabric) {
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(mCaughtExceptionHandler);
}
#Override
public void failure(Exception e) {
}
})
.build());
Yes, it is possible.
In your Application class:
#Override
public void onCreate() {
super.onCreate();
Crashlytics.start(this);
initUncaughtExceptionHandler();
}
private void initUncaughtExceptionHandler() {
final ScheduledThreadPoolExecutor c = new ScheduledThreadPoolExecutor(1);
c.schedule(new Runnable() {
#Override
public void run() {
final UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
// do my amazing stuff here
System.err.println("Error!");
//then pass the job to the previous handler
defaultHandler.uncaughtException(paramThread, paramThrowable);
}
});
}
}, 5, TimeUnit.SECONDS);
}
The reason I'm scheduling this after 5 seconds is because Crashlytics needs some time to set up his stuff. I'm using this code and it works perfectly. Of course if your app crashes on start, sorry but no custom handler ;)
None if those solutions worked for me. I did it like following:
// Setup handler for uncaught exceptions.
Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
{
#Override
public void uncaughtException (Thread thread, Throwable e)
{
//Send Report to Crashlytics. Crashlytics will send it as soon as it starts to work
Crashlytics.logException(e);
//Your custom codes to Restart the app or handle this crash
HandleCrashes(thread, e);
}
});
And here is my Custom Method to restart the APP:
private void HandleCrashes(Thread t, Throwable e) {
Intent i = new Intent(mContext, frmLogin.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("JustLogin", true);
startActivity(i);
System.exit(1);
}
Turn off automatic collection
add this to your AndroidManifest.xml
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
Register your custome UncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionHandler(customerUncaughtExceptionHandler)
Manually start Crashlytics after registered UncaughtExceptionHandler
Fabric.with(this, new Crashlytics());
rebuild and reinstall your Application
I found a solution for Fabric 2.10.1:
Thread.setDefaultUncaughtExceptionHandler(yourExceptionHandler)
Fabric.with(this, Crashlytics())
I want to send a firebase crash report for every uncaught exception in my app but I also want to Android system to show the "App has stopped responding" dialog. How do I go about this?? I already have an UncaughtException handler that sends the firebase crash report. My issue now is letting Android handle the rest of the process.
The uncaught exception can be delegated back to system by storing the old exception handler and passing uncaught exceptions to it.
First create an Application class as below:
public class Controller extends Application {
private static Thread.UncaughtExceptionHandler defaultHandler;
#Override
public void onCreate() {
super.onCreate();
if (defaultHandler == null) {
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
}
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread t, Throwable e) {
FirebaseCrash.report(e); //or whatever
defaultHandler.uncaughtException(t, e); //this will show crash dialog.
}
});
}
}
Then use this class as application in the manifest as:
<application
android:name=".Controller"
... />
It works on Android 5.0, but it won't work on Jellybean devices.
#ReportsCrashes(
formKey = "", // This is required for backward compatibility but not used
formUri = "dummyurl",
reportType = org.acra.sender.HttpSender.Type.JSON,
httpMethod = org.acra.sender.HttpSender.Method.PUT,
formUriBasicAuthLogin="adminTest",
formUriBasicAuthPassword="adminTest",
mode = ReportingInteractionMode.TOAST,
resToastText = R.string.crash_toast_text)
public class Application extends Application {
#Override
public void onCreate() {
super.onCreate();
init();
ACRA.init(this);
Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
}
private UncaughtExceptionHandler sUncaughtExceptionHandler = new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.i(LogTag, "uncaughtException: ");
dummyMethod();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(
thread, ex);
}
};
}
ACRA sets its own UncaughtExceptioHandler that performs the error reporting and then delegates to any UncaughtExceptionHandler that existed before it.
So if you want your UncaughtExceptionHandler to be called once ACRA has completed the error reporting, then you need to set your exception handler before calling ACRA.init(this);
Since you are sending Toast notifications you also need to set forceCloseDialogAfterToast as the assumption is that the defaultExceptionHandler is the one from Android framework that will display a force close dialog and you wouldn't want to show that if also showing a Toast.
I'm trying to use Flurry's onEvent method in my custom uncaught exception handler (UEH) but the events aren't showing up and I'm thinking that it might be because by the time it's gotten to the exception handler the flurry session has ended.
I make the call to FlurryAgent.onStartSession in the onStart() method of every activity and calling FlurryAgent.onEndSession() in the onStop() method of every activity.
I'm setting my UEH in my first activity:
Thread.setDefaultUncaughtExceptionHandler(new TopExceptionHandler(this));
My UEH (simplified) looks like:
public class TopExceptionHandler implements UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler defaultUEH;
private Activity app = null;
public int numberOfStories = -1;
public TopExceptionHandler(Activity app) {
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
this.app = app;
}
#Override
public void uncaughtException(Thread t, Throwable e) {
Map<String, String> params = new HashMap<String, String>();
ExceptionHandlerValues values = ExceptionHandlerValues.getExceptionHandlerValues();
params.put("model", values.model);
params.put("androidVersion", values.androidVersion);
params.put("androidSDK", values.androidSDK);
params.put("wattpadVersion", values.wattpadVersion);
params.put("misc", "StoryCount=" + values.storyCount + ";");
params.put("class", e.getClass().toString());
// Send it off to Flurry as an event
FlurryAgent.onEvent(ExceptionHandlerValues.EVENT_ID, params);
defaultUEH.uncaughtException(t, e);
}
}
If the flurry session has ended (which I'm kind of thinking it has) how can I start a new one in my UEH? I've seen other people say they've successfully implemented this solution so I know it's possible, but I can't seem to get it.
Thanks!
I ended up starting a new flurry session in my UEH using the application context. Events are now being sent and recorded which makes debugging problems our users are experiencing much easier.
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.
Basic Example for someone who comes to this page with a solution :)
public class ChildActivity extends BaseActivity {
#SuppressWarnings("unused")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int a=1/0;
}
}
Class for handling error:
public class BaseActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
Log.e("Alert","Lets See if it Works !!!");
}
});
}
}
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
Code:
public class BaseActivity extends Activity {
#Override
public void onCreate() {
super.onCreate();
final Thread.UncaughtExceptionHandler oldHandler =
Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(
Thread paramThread,
Throwable paramThrowable
) {
//Do your own error handling here
if (oldHandler != null)
oldHandler.uncaughtException(
paramThread,
paramThrowable
); //Delegates to Android's error handling
else
System.exit(2); //Prevents the service/app from freezing
}
});
}
}
For those who just want to see exception details when your app crashes on device (in debug config). This is application class:
private Thread.UncaughtExceptionHandler oldHandler;
#Override
public void onCreate() {
super.onCreate();
if (!BuildConfig.DEBUG)
return;
oldHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
try {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
Intent intent = new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_TEXT, sw.toString());
intent.setType("text/plain");
startActivity(intent);
} catch(Exception ex) {
ex.printStackTrace();
} finally {
if (oldHandler != null)
oldHandler.uncaughtException(t, e);
else
System.exit(1);
}
});
}
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:
BaseActivity
|
_______________________
| | |
Activity A Activity B Activity C
final Thread.UncaughtExceptionHandler defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable e) {
handleUncaughtException(thread, e, defaultEH);
}
});
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:
The specified custom code (in my case writing to the log file) will be called multiple times, which is not the desired action.
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:
private static boolean customExceptionHandlerAttached = false;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!customExceptionHandlerAttached) {
final Thread.UncaughtExceptionHandler defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable e) {
handleUncaughtException(thread, e, defaultEH);
}
});
customExceptionHandlerAttached = true;
}
}
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()
if you want use this library
https://github.com/selimtoksal/Android-Caught-Global-Exception-Library
create your TransferObject not all in your activities just use in Base activity