Convert Figma logo to code with AI

Ereza logoCustomActivityOnCrash

Android library that allows launching a custom activity when your app crashes, instead of showing the hated "Unfortunately, X has stopped" dialog.

3,735
543
3,735
7

Top Related Projects

A memory leak detection library for Android.

BugSnag crash monitoring and reporting tool for Android apps

Render After Effects animations natively on Android and iOS, Web, and React Native

Quick Overview

CustomActivityOnCrash is a library for Android that allows developers to easily handle application crashes and provide a custom activity to display an error message to the user instead of the default Android crash dialog. This can be useful for providing a more user-friendly experience when an app crashes.

Pros

  • Customizable Error Message: The library allows developers to customize the error message and other UI elements displayed to the user when the app crashes.
  • Automatic Crash Handling: The library automatically handles crashes and displays the custom activity, reducing the amount of boilerplate code needed.
  • Flexible Configuration: The library provides various configuration options to customize the behavior and appearance of the custom crash activity.
  • Compatibility: The library is compatible with Android versions from API level 14 (Ice Cream Sandwich) and above.

Cons

  • Dependency: The project requires the inclusion of an external library, which may increase the overall size of the application.
  • Potential Performance Impact: The custom crash activity may have a slight performance impact compared to the default Android crash dialog.
  • Limited Customization: While the library provides various configuration options, the level of customization may be limited compared to implementing a custom crash handling solution from scratch.
  • Potential Compatibility Issues: The library may not be compatible with all Android devices or versions, which could lead to issues in certain scenarios.

Code Examples

Here are a few examples of how to use the CustomActivityOnCrash library:

  1. Initializing the Library:
CustomActivityOnCrash.install(this);

This code initializes the library and sets it up to handle crashes in the application.

  1. Configuring the Crash Activity:
CustomActivityOnCrash.setCustomActivityOnCrashConfiguration(new CrashConfig.Builder()
    .errorDrawable(R.drawable.custom_error_image)
    .eventListener(new EventListener() {
        @Override
        public void onLaunchErrorActivity(Throwable throwable) {
            // Handle the crash event
        }
    })
    .build());

This code configures the custom crash activity, setting a custom error image and an event listener to handle the crash event.

  1. Providing a Custom Error Message:
CustomActivityOnCrash.setDefaultErrorActivityDrawable(R.drawable.custom_error_image);
CustomActivityOnCrash.setDefaultErrorMessage(this, "Oops, something went wrong!");

This code sets a custom error message and a custom error image to be displayed in the crash activity.

  1. Handling Crashes in a Custom Application Class:
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        CustomActivityOnCrash.install(this);
    }
}

This code initializes the CustomActivityOnCrash library in a custom Application class, ensuring that crashes are handled throughout the entire application.

Getting Started

To get started with the CustomActivityOnCrash library, follow these steps:

  1. Add the library to your project's dependencies:
dependencies {
    implementation 'cat.ereza:customactivityoncrash:2.3.0'
}
  1. Initialize the library in your application's onCreate() method:
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        CustomActivityOnCrash.install(this);
    }
}
  1. (Optional) Configure the custom crash activity by setting the desired options:
CustomActivityOnCrash.setCustomActivityOnCrashConfiguration(new CrashConfig.Builder()
    .errorDrawable(R.drawable.custom_error_image)
    .eventListener(new EventListener() {
        @Override
        public void onLaunchErrorActivity(Throwable throwable) {
            // Handle the crash event
        }
    })
    .build());
  1. (Optional) Set a custom error message and image:
CustomActivityOnCrash.setDefaultErrorActivityDrawable(R.drawable.custom_error_image);
CustomActivityOnCrash.setDefaultErrorMessage(this, "Oops, something went wrong

Competitor Comparisons

A memory leak detection library for Android.

Pros of LeakCanary

  • LeakCanary is a powerful tool for detecting memory leaks in Android applications, which can be a common and difficult-to-diagnose issue.
  • It provides detailed reports and visualizations of memory leaks, making it easier to identify and fix the root cause.
  • LeakCanary is actively maintained and has a large community of contributors, ensuring its continued development and support.

Cons of LeakCanary

  • LeakCanary can have a larger impact on the app's performance and memory usage compared to CustomActivityOnCrash, as it requires additional runtime overhead for its memory monitoring functionality.
  • The integration process for LeakCanary can be more complex, as it requires additional configuration and setup steps.

Code Comparison

CustomActivityOnCrash:

public class CustomActivityOnCrash {
    public static void install(Application application) {
        Thread.setDefaultUncaughtExceptionHandler(new CustomUncaughtExceptionHandler(application));
    }
}

LeakCanary:

public class LeakCanary {
    public static void install(Application application) {
        RefWatcher refWatcher = LeakCanary.install(application);
        // Additional configuration and setup steps
    }
}

BugSnag crash monitoring and reporting tool for Android apps

Pros of Bugsnag

  • Bugsnag provides a comprehensive error reporting and monitoring solution, with support for a wide range of platforms and programming languages.
  • The Bugsnag Android SDK offers advanced features like breadcrumbs, user tracking, and custom metadata, which can provide valuable insights for debugging and troubleshooting.
  • Bugsnag's web-based dashboard and reporting tools offer a user-friendly interface for analyzing and managing errors.

Cons of Bugsnag

  • Bugsnag is a commercial product, and the pricing model may not be suitable for all projects, especially smaller or budget-constrained ones.
  • The Bugsnag SDK can add some overhead to the app's codebase and may require additional configuration and setup.
  • Depending on the project's requirements, the advanced features of Bugsnag may not be necessary, and a simpler solution like CustomActivityOnCrash may be more appropriate.

Code Comparison

CustomActivityOnCrash:

public class CustomActivityOnCrash {
    public static void install(Application application) {
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable throwable) {
                Intent intent = new Intent(application, YourCustomErrorActivity.class);
                intent.putExtra(YourCustomErrorActivity.EXTRA_EXCEPTION, throwable);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                application.startActivity(intent);
                System.exit(1);
            }
        });
    }
}

Bugsnag:

public class BugsnagClient {
    public static void start(Application application, String apiKey) {
        Bugsnag.start(application, apiKey);
    }

    public static void notify(Throwable throwable) {
        Bugsnag.notify(throwable);
    }

    public static void leaveBreadcrumb(String message) {
        Bugsnag.leaveBreadcrumb(message);
    }
}

The key difference is that CustomActivityOnCrash provides a simple way to display a custom error activity when an exception occurs, while Bugsnag offers a more comprehensive error reporting and monitoring solution with additional features like breadcrumbs and user tracking.

Render After Effects animations natively on Android and iOS, Web, and React Native

Pros of Lottie-Android

  • Lottie-Android is a powerful library that allows for the easy integration of complex animations into Android applications, making it a valuable tool for creating visually appealing and engaging user interfaces.
  • The library provides a wide range of features, including support for vector graphics, the ability to control animation playback, and the option to customize animations to fit specific design requirements.
  • Lottie-Android is actively maintained by the Airbnb team, ensuring regular updates and bug fixes, which is important for the long-term viability and reliability of the library.

Cons of Lottie-Android

  • Lottie-Android may have a steeper learning curve compared to CustomActivityOnCrash, as it requires a more in-depth understanding of animation concepts and the Lottie file format.
  • The library's size and complexity may make it less suitable for smaller projects or applications with limited resources, as it can add significant overhead to the application's size and performance.
  • Lottie-Android may not be as well-suited for simple or basic animation tasks, where a more lightweight solution like CustomActivityOnCrash might be more appropriate.

Code Comparison

Lottie-Android (Airbnb):

LottieAnimationView animationView = findViewById(R.id.animation_view);
animationView.setAnimation("animation.json");
animationView.playAnimation();

CustomActivityOnCrash (Ereza):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_custom_crash);
}

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Custom Activity On Crash library

This library allows launching a custom activity when the app crashes, instead of showing the hated "Unfortunately, X has stopped" dialog.

How to use

One-step install

Add the following dependency to your build.gradle:

dependencies {
    implementation 'cat.ereza:customactivityoncrash:2.4.0'
}

...and you are done!

You can combine this library with other crash handlers such as Firebase Crashlytics or ACRA.

If you are using Firebase Crashlytics, you must call FirebaseApp.initializeApp(this); inside onCreate in your Application class.

If you are using ACRA, you must initialize it inside onCreate in your Application class, instead of attachBaseContext, and enable alsoReportToAndroidFramework, otherwise, CustomActivityOnCrash will not work.

Try it

Force an app crash by throwing an uncaught exception, using something like this in your code:

throw new RuntimeException("Boom!");

Advanced setup

You can customize the behavior of this library in several ways by setting its configuration at any moment. However, it's recommended to do it on your Application class so it becomes available as soon as possible.

Add a snippet like this to your Application class:

@Override
public void onCreate() {
    super.onCreate();

    CaocConfig.Builder.create()
        .backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT) //default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM
        .enabled(false) //default: true
        .showErrorDetails(false) //default: true
        .showRestartButton(false) //default: true
        .logErrorOnRestart(false) //default: true
        .trackActivities(true) //default: false
        .minTimeBetweenCrashesMs(2000) //default: 3000
        .errorDrawable(R.drawable.ic_custom_drawable) //default: bug image
        .restartActivity(YourCustomActivity.class) //default: null (your app's launch activity)
        .errorActivity(YourCustomErrorActivity.class) //default: null (default error activity)
        .eventListener(new YourCustomEventListener()) //default: null
        .customCrashDataCollector(new YourCustomCrashDataCollector()) //default: null
        .apply();

    //If you use Firebase Crashlytics or ACRA, please initialize them here as explained above.
}

Customization options

Custom behavior

Here is a more detailed explanation of each option that can be set using CaocConfig.Builder:

launchWhenInBackground(int);

This method defines if the error activity should be launched when the app crashes while on background. There are three modes:

  • CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM: launch the error activity even if the app is in background.
  • CaocConfig.BACKGROUND_MODE_CRASH: launch the default system error when the app is in background.
  • CaocConfig.BACKGROUND_MODE_SILENT: crash silently when the app is in background.

The default is CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM.

enabled(boolean);

Defines if CustomActivityOnCrash crash interception mechanism is enabled. Set it to true if you want CustomActivityOnCrash to intercept crashes, false if you want them to be treated as if the library was not installed. This can be used to enable or disable the library depending on flavors or buildTypes. The default is true.

showErrorDetails(boolean);

This method defines if the error activity must show a button with error details. If you set it to false, the button on the default error activity will disappear, thus disabling the user from seeing the stack trace. The default is true.

trackActivities(boolean);

This method defines if the library must track the activities the user visits and their lifecycle calls. This is displayed on the default error activity as part of the error details. The default is false.

showRestartButton(boolean);

This method defines if the error activity must show a "Restart app" button or a "Close app" button. If you set it to false, the button on the default error activity will close the app instead of restarting. If you set it to true and your app has no launch activity, it will still display a "Close app" button! The default is true.

logErrorOnRestart(boolean);

This controls if the stack trace must be relogged when the custom error activity is launched. This functionality exists because the Android Studio default Logcat view only shows the output for the current process. This makes it easier to see the stack trace of the crash. You can disable it if you don't want an extra log. The default is true.

minTimeBetweenCrashesMs(boolean);

Defines the time that must pass between app crashes to determine that we are not in a crash loop. If a crash has occurred less that this time ago, the error activity will not be launched and the system crash screen will be invoked. The default is 3000.

errorDrawable(Integer);

This method allows changing the default upside-down bug image with an image of your choice. You can pass a resource id for a drawable or a mipmap. The default is null (the bug image is used).

restartActivity(Class<? extends Activity>);

This method sets the activity that must be launched by the error activity when the user presses the button to restart the app. If you don't set it (or set it to null), the library will use the first activity on your manifest that has an intent-filter with action cat.ereza.customactivityoncrash.RESTART, and if there is none, the default launchable activity on your app. If no launchable activity can be found and you didn't specify any, the "restart app" button will become a "close app" button, even if showRestartButton is set to true.

As noted, you can also use the following intent-filter to specify the restart activity:

<intent-filter>
    <!-- ... -->
    <action android:name="cat.ereza.customactivityoncrash.RESTART" />
</intent-filter>
errorActivity(Class<? extends Activity>);

This method allows you to set a custom error activity to be launched, instead of the default one. Use it if you need further customization that is not just strings, colors or themes (see below). If you don't set it (or set it to null), the library will use the first activity on your manifest that has an intent-filter with action cat.ereza.customactivityoncrash.ERROR, and if there is none, a default error activity from the library. If you use this, the activity must be declared in your AndroidManifest.xml, with process set to :error_activity.

Example:

<activity
    android:name="cat.ereza.customactivityoncrash.sample.CustomErrorActivity"
    android:label="@string/error_title"
    android:process=":error_activity" />

As noted, you can also use the following intent-filter to specify the error activity:

<intent-filter>
    <!-- ... -->
    <action android:name="cat.ereza.customactivityoncrash.ERROR" />
</intent-filter>
eventListener(EventListener);

This method allows you to specify an event listener in order to get notified when the library shows the error activity, restarts or closes the app. The EventListener you provide can not be an anonymous or non-static inner class, because it needs to be serialized by the library. The library will throw an exception if you try to set an invalid class. If you set it to null, no event listener will be invoked. The default is null.

customCrashDataCollector(CustomCrashDataCollector);

This method allows you to specify a custom crash data collector that will be invoked when a crash occurs. This additional data will be added to the "error details" view on the default error activity, or you can use it in your custom error activity. The CustomCrashDataCollector you provide can not be an anonymous or non-static inner class, because it needs to be serialized by the library. The library will throw an exception if you try to set an invalid class. If you set it to null, no custom crash data will be collected. The default is null.

Customization of the default activity

You can override several resources to customize the default activity:

Theme:

The activity uses your application theme by default. It must be a child of Theme.AppCompat or a default one will be used. If you want to specify a specific theme only for the error activity, you can do so by redeclaring it on your manifest like this:

<activity
    android:name="cat.ereza.customactivityoncrash.activity.DefaultErrorActivity"
    android:theme="@style/YourThemeHere"
    android:process=":error_activity" />

Image:

By default, an image of a bug is displayed. You can change it to any image by using the provided errorDrawable(int) method. You can also do it the old way and create a customactivityoncrash_error_image drawable on all density buckets (mdpi, hdpi, xhdpi, xxhdpi and xxxhdpi).

Strings:

You can provide new strings and translations for the default error activity strings by overriding the following strings:

<string name="customactivityoncrash_error_activity_error_occurred_explanation">An unexpected error occurred.\nSorry for the inconvenience.</string>
<string name="customactivityoncrash_error_activity_restart_app">Restart app</string>
<string name="customactivityoncrash_error_activity_close_app">Close app</string>
<string name="customactivityoncrash_error_activity_error_details">Error details</string>
<string name="customactivityoncrash_error_activity_error_details_title">Error details</string>
<string name="customactivityoncrash_error_activity_error_details_close">Close</string>
<string name="customactivityoncrash_error_activity_error_details_copy">Copy to clipboard</string>
<string name="customactivityoncrash_error_activity_error_details_copied">Copied to clipboard</string>
<string name="customactivityoncrash_error_activity_error_details_clipboard_label">Error information</string>

There is a sample project module with examples of these overrides. If in doubt, check the code in that module.

Completely custom error activity

If you choose to create your own completely custom error activity, you can use these methods:

CustomActivityOnCrash.getAllErrorDetailsFromIntent(getIntent());

Returns several error details including the stack trace that caused the error, the activity log (if available) and the custom crash data (if available), as a string. This is used in the default error activity error details dialog.

CustomActivityOnCrash.getStackTraceFromIntent(getIntent());

Returns the stack trace that caused the error as a string.

CustomActivityOnCrash.getActivityLogFromIntent(getIntent());

Returns the activity log as a string if trackActivities was enabled, null otherwise.

CustomActivityOnCrash.getCustomCrashDataFromIntent(getIntent());

Returns the custom crash data collected with your CustomCrashDataCollector if customCrashDataCollector was enabled, null otherwise.

CustomActivityOnCrash.getConfigFromIntent(getIntent());

Returns the config of the library when the crash happened. Used to call some methods.

CustomActivityOnCrash.restartApplication(activity, config);

Kills the current process and restarts the app again with a startActivity() to the passed intent. You MUST call this to restart the app, or you will end up having several Application class instances and experience multiprocess issues.

CustomActivityOnCrash.restartApplicationWithIntent(activity, intent, config);

The same as CustomActivityOnCrash.restartApplication, but allows you to specify a custom intent.

CustomActivityOnCrash.closeApplication(activity, eventListener);

Closes the app and kills the current process. You MUST call this to close the app, or you will end up having several Application class instances and experience multiprocess issues.

The sample project module includes an example of a custom error activity. If in doubt, check the code in that module.

Using Proguard?

No need to add special rules, the library should work even with obfuscation.

Inner workings

This library relies on the Thread.setDefaultUncaughtExceptionHandler method. When an exception is caught by the library's UncaughtExceptionHandler it does the following:

  1. Captures the stack trace that caused the crash
  2. Launches a new intent to the error activity in a new process passing the crash info as an extra.
  3. Kills the current process.

The inner workings are based on ACRA's dialog reporting mode with some minor tweaks. Look at the code if you need more detail about how it works.

Incompatibilities

  • CustomActivityOnCrash will not work with any custom UncaughtExceptionHandler set after initializing the library that does not call back to the original handler.
  • If your app initialization or error activity crash, there is a possibility of entering an infinite restart loop (this is checked by the library for the most common cases, but could happen in rarer cases).
  • The library has not been tested with multidex enabled. It uses Class.forName() to load classes, so maybe that could cause some problem in API<21. If you test it with such configuration, please provide feedback!
  • The library has not been tested with multiprocess apps. If you test it with such configuration, please provide feedback too!

Disclaimers

  • This will not avoid ANRs from happening.
  • This will not catch native errors.
  • There is no guarantee that this will work on every device.
  • This library will not make you toast for breakfast :)

Contributing & license

Any contribution in order to make this library better will be welcome!

The library is licensed under the Apache License 2.0.

The bug image used in the default error activity is licensed under CC-BY by Riff: https://www.sketchport.com/drawing/6119265933459456/lady-bug If you use the image in your app, don't forget to mention that!