Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Crashlytics With UncaughtExceptionHandler

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); 
  } 
}; 

} 
like image 373
Durgesh Patel Avatar asked May 29 '18 13:05

Durgesh Patel


People also ask

Is Crashlytics deprecated?

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.

Is Firebase Crashlytics real time?

Firebase Crashlytics, a real time crash reporting tool, helps you prioritize and fix your most pervasive crashes based on the impact on real users.


2 Answers

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)
    }
like image 167
Eugene P. Avatar answered Sep 20 '22 15:09

Eugene P.


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.
            }
        }
    }
like image 20
android user Avatar answered Sep 24 '22 15:09

android user