I have integrated Firebase Crashlytics version 2.9.1 to digging out crashes to cover performance and stability of my app.
Crashes are not being logged on firebase crashlytics console if application is having own UncaughtExceptionHandler.
I have BaseActivity in my app. inside onCreate() method I have registered custom UncaughtExceptionHandler based on project requirement.
Whenever app got crashed due to any reason, user should be redirected to splash screen (MainActivity.java
) .
public class BaseActivity extends FragmentActivity{
@Override
protected void onCreate(Bundle arg0) {
// Enable global crash handler.
Thread.setDefaultUncaughtExceptionHandler(handleAppCrash);
}
/***
* @Purpose Called when any crash occurs in the application.
***/
private Thread.UncaughtExceptionHandler handleAppCrash = new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Intent intent = new Intent(context, MainActivity.class); //redirect to Splash screen
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
System.exit(0);
}
};
}
Old versions of your app still using the Fabric Crashlytics SDK will not break once it's deprecated, however they will no longer submit crash reports. But it seems like it will just continue to work as per normal after this date until further notice.
Firebase Crashlytics, a real time crash reporting tool, helps you prioritize and fix your most pervasive crashes based on the impact on real users.
OK, I've investigated this question.
You should not create custom exception handler inside BaseActivity. It is better to do it in your Application class. In case of BaseActivity you will heve a new handler each time you start new activity, which extends your BaseActivity.
So, in your application class onCreate() method you can get default application handler
val defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
In my case it is an instance of a
com.crashlytics.android.core.CrashlyticsUncaughtExceptionHandler
This "defaultExceptionHandler" you will use to sends correct errors to firebase. Then you can create your own exception handler, but need to hold here this "defaultExceptionHandler".
class DefaultExceptionHandler (private val defaultExceptionHandler:Thread.UncaughtExceptionHandler?) : Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, ex: Throwable) {
try {
// here you restore your activity and do other things
// and of course you deal with defaultExceptionHandler
// without it firebase will not work
defaultExceptionHandler?.uncaughtException(thread, ex)
// only after firebase dealed with an exception, you can exit
System.exit(0)
} catch (e: IOException) {
// just catch
}
}
}
And finally firebase will show you crashes as it is needed. Application onCreate() example
override fun onCreate() {
super.onCreate()
Fabric.with(this, Crashlytics())
val defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
val customExceptionHandler = DefaultExceptionHandler(defaultExceptionHandler)
Thread.setDefaultUncaughtExceptionHandler(customExceptionHandler)
}
I also had the same problem in my app. As suggested by @niyas I also used the same solution and it's working fine for me. Firebase using the content provider to initialize firebase crashlytics sdk and its uncaught exception handler. So if you register your uncaught exception handler in a custom content provider with higher priority then the firebase one. Then the order of receiving a callback to the handler is reversed. First the callback is going to Firebase handler and then to yours's handler. In this way firebase collects the stack traces for the crashes and you also get the callback in handler to relaunch the app / or any other use case. I found this solution on github Here's a link!
Code snippet:
<application>
...
<!-- Firebase SDK initOrder is 100. Higher order init first -->
<provider
android:name=".UncaughtExceptionHandlerContentProvider"
android:authorities="${applicationId}"
android:exported="false"
android:initOrder="101"
android:grantUriPermissions="false" />
...
</application>
public class UncaughtExceptionHandlerContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
MyCustomCrashHandler myHandler = new MyCustomCrashHandler(Thread.getDefaultUncaughtExceptionHandler());
Thread.setDefaultUncaughtExceptionHandler(myHandler);
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return null; }
@Nullable
@Override
public String getType(@NonNull Uri uri) { return null; }
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null; }
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; }
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; }
}
public class MyCustomCrashHandler implements UncaughtExceptionHandler {
@Nullable
private final UncaughtExceptionHandler defaultHandler;
public MyCustomCrashHandler(@Nullable UncaughtExceptionHandler defaultHandler)(){
this.defaultHandler = defaultHandler;
}
@Override
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable ex) {
// We are now safely being called after Crashlytics does its own thing.
// Whoever is the last handler on Thread.getDefaultUncaughtExceptionHandler() will execute first on uncaught exceptions.
// Firebase Crashlytics will handle its own behavior first before calling ours in its own 'finally' block.
// You can choose to propagate upwards (it will kill the app by default) or do your own thing and propagate if needed.
try {
//do your own thing.
} catch (Exception e) {
e.printStackTrace();
} finally {
if (defaultHandler != null) {
defaultHandler.uncaughtException(thread, ex)
// propagate upwards. With this workaround (and also without any other similar UncaughtExceptionHandler based on ContentProvider),
// defaultHandler should now be an instance of com.android.internal.os.RuntimeInit.KillApplicationHandler
// hence properly killing the app via framework calls.
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With