Reporting Crashes for Android

Covered here are APIs relevant to reporting crashes for your Android apps.

🚧

Privacy Policy

It is highly recommended to mention in your privacy policy that you may be collecting logging data in order to assist with troubleshooting crashes.

Android Crash Types

  1. Crash: Fatal crashes refer to an error or issue that causes the app to terminate unexpectedly, meaning the app completely shuts down and is no longer usable until the user restarts it. These crashes interrupt the user experience, as the app cannot recover from the issue on its own and must be relaunched.

    Fatal crashes are the most severe type of crashes, and they generate crash reports that help developers investigate what caused the app to crash. Typically, they occur due to unhandled errors, system conflicts, or serious resource issues. Here are the types of fatal crashes:

    1. Native crashes: Caused by issues in the native layer of an app, often written in C or C++ (e.g., NDK crashes). These involve signals like SIGSEGV, SIGBUS, or SIGABRT, similar to iOS.
    2. Java exceptions: Unhandled exceptions in the Java layer, such as NullPointerException, ArrayIndexOutOfBoundsException, or IllegalArgumentException, that cause the app to crash.

  2. ANR (Application Not Responding): When the main thread is blocked for too long, leading to an ANR error. This is an Android-specific type where the system prompts the user to either wait or force close the app.

  3. Non-Fatal: Non-fatal crashes refer to an error or issue that occurs in the app but doesn’t cause the app to completely shut down or crash. Instead, the app encounters a problem, such as an exception or unexpected behavior, but is able to continue running without quitting. Non-fatal crashes are useful for developers because they provide insights into bugs or problems that need fixing before they turn into full-blown crashes.

With Instabug Crash Reporting there are two ways to have your application report a crash, either automatically or manually. After the crash is sent to your dashboard, you can sort and filter for specific crashes easily.

Automatic Crash Reporting

If you enable Crash Reporting, crashes will automatically be reported to and viewable from the crashes page of your Instabug dashboard.

You'll also see the trends covering the previous 7 days, including:

  • Crash-free sessions: the percentage of sessions that ran and concluded without any fatal errors.
  • Crash-free users: the percentage of users that haven't encountered any fatal errors.
  • Crashing sessions: the number of sessions that ran and concluded with a fatal error.
  • Affected users: the number of unique users who had one or more sessions that ended with a fatal error.
  • Total number of occurrences: by hovering on it, you’ll get a breakdown of the number of fatal sessions, the number of OOM sessions, the number of ANR sessions, and the number of non-fatal sessions.

If there is a sharp decline in the crash-free sessions rate, an email will be sent to notify you.

This is the crashes page of the Instabug dashboard.

This is the crashes page of the Instabug dashboard.

ANR Crashes

By default, if Crash Reporting is enabled, Instabug captures any ANR that occurs within your app, along with the stack trace of the crash.

You can disable/enable ANR reporting using the following API:

CrashReporting.setAnrState(Feature.State.DISABLED)
CrashReporting.setAnrState(Feature.State.DISABLED);

Force Restarts

Starting from SDK version 11.1.0, Instabug automatically reports Force Restarts. A Force Restart is when a user force terminates your application and re-launches it within 5 seconds, which could indicate performance issues.

Please note that Force Restarts reports will not contain a stack trace.

For Force Restarts to be detected on devices running Android KitKat (API 20) and below, the following security permission needs to be added:

<uses-permission android:name="android.permission.GET_TASKS"/>

App Hangs

Starting SDK version 11.5.0, Instabug automatically reports App Hangs. An App Hang is captured when the main thread is blocked for more than 3 seconds. App Hangs that last more than 3 seconds are considered severe and are likely to cause user frustration. They are reported along with a stack trace for debugging.

Manual Crash Reporting

You can offer your users Instabug Bug Reporting to report bugs. However, you can use the following method and API to manually report any error or exception that you handle in your code and assign it a severity level.

Report Exception

To report exceptions manually, use the following method; Both errors and exceptions can be passed to this method:

val exception = IBGNonFatalException.Builder(NullPointerException("Test Exception"))
.setUserAttributes(mapOf("height" to "tall"))
.setFingerprint("My Custom Fingerprint")
.setLevel(IBGNonFatalException.Level.CRITICAL)
.build()
CrashReporting.report(exception)
IBGNonFatalException exception = new IBGNonFatalException.Builder(new NullPointerException("Test Exception"))
.setUserAttributes(new HashMap<>())
.setFingerprint("My Custom Fingerprint")
.setLevel(IBGNonFatalException.Level.CRITICAL)
.build();
CrashReporting.report(exception);

Here is another example:

IBGNonFatalException.Builder(NullPointerException("Test Exception"))
.setUserAttributes(mapOf("height" to "tall"))
.setFingerprint("My Custom Fingerprint")
.setLevel(IBGNonFatalException.Level.CRITICAL)
.build().let { exception -> CrashReporting.report(exception) }

Add Level to Exception

You can set different severity levels for manually reported exceptions using the following API:

val exception = IBGNonFatalException.Builder(NullPointerException("Test Exception"))
.setLevel(IBGNonFatalException.Level.CRITICAL)
.build()
CrashReporting.report(exception)
IBGNonFatalException exception = new IBGNonFatalException.Builder(new NullPointerException("Test Exception"))
.setLevel(IBGNonFatalException.Level.CRITICAL)
.build();
CrashReporting.report(exception);

Here are the different severity levels available. In case no level is indicated, the default level would be ERROR

IBGNonFatalException.Level.WARNING
IBGNonFatalException.Level.ERROR
IBGNonFatalException.Level.CRITICAL
IBGNonFatalException.Level.INFO

NDK Crashes

In the events that you're using a C++/NDK library or have code that runs at C++ level, the Instabug SDK will automatically detect and capture these low level crashes.

Adding the NDK Crashes Dependency

In order to start capturing NDK crashes, you'll need to add the below dependency to your app level gradle. Once it's added and the gradle is synced, NDK crashes will automatically be captured after the SDK is initialized and NDK crash reporting is enabled.

implementation 'com.instabug.library:instabug-ndk-crash:14.0.0'

Enabling and Disabling

NDK crash reporting is disabled by default if the NDK dependency is added, however it can be enabled using the below method.

CrashReporting.setNDKCrashesState(Feature.State.ENABLED)
CrashReporting.setNDKCrashesState(Feature.State.ENABLED);

Deobfuscation

Since native code is always obfuscated, you'll need to follow the instructions mentioned in the deobfuscation page in order to make the stack traces more readable.

Grouping

When an already existing crash occurs once more for any user, that crash is reported as an occurrence in the original entry. However, in order to calculate whether a crash already exists and needs to be grouped, Instabug generates a fingerprint based on attributes used in the grouping logic.

The default Instabug grouping algorithm uses a mix of the exception and stack trace information. In some cases, you might want to change how the issues are grouped together using custom grouping or fingerprints.

Custom Grouping

🚧

Required Mapping Files

Please note that in order for custom grouping to be applied, mapping files are required to be uploaded first, otherwise, default grouping will be applied. For more information on uploading mapping files, please visit the deobfuscation page.

One way to customize how crashes are grouped together is by providing Instabug with packages that you would like to ignore from our default grouping logic. If you define a package to be ignored, the frame with that package will be skipped and then we'll move on to find the next one that is not on your ignored list. This is done on an application level by going to your Application → Settings → Custom Crash Grouping. A few examples of this can be found below with an expected input.

Expected Input:

  • Package
  • Packages will be matched on a "starts with" basis:
    - Adding com.thirdParty will ignore any package we find in the stack trace that start with com.thirdParty
    - Examples: com.thirdParty.start, com.thirdParty.start.user, com.thirdParty.end etc.

Example 1
Sample Stack Trace:

com.thirdParty.login
com.app.page
com.android.error
com.app.user
com.app.homepageloading
  • Without custom grouping, Instabug would group the crash based on com.thirdParty.login since it's the first non-system frame
  • With custom grouping, having com.thirdParty.login on your list of package to ignore, we will skip the first frame com.thirdParty.log and the crash will be grouped based on com.app.page

Example 2
Sample Stack Trace:

com.thirdParty.login
com.thirdParty.userlogged
com.app.page
com.android.error
com.app.user
com.app.homepageloading
  • Without custom grouping, Instabug would group the crash based on com.thirdParty.login since it's the first non-system frame
  • With custom grouping, having com.thirdParty on your list of package to ignore, we will skip the first two frames [com.thirdParty.login and com.thirdParty.userlogged] and the crash will be grouped based on com.app.page

Custom Fingerprinting

🚧

Overriding the default grouping

Please note that using custom fingerprinting will override Instabug's default grouping by sending a fingerprint string.

In the event that you'd like to report the exception manually with a custom grouping fingerprint in mind, you can use the below APIs to do just that.

CrashReporting.reportException(throwable, "exception identifier", userAttrsMap, "grouping fingerprint");
CrashReporting.reportException(throwable, "exception identifier", userAttrsMap, "grouping fingerprint")

Crashes List

This section contains a list of all the crashes that have been reported by your application. The title of each crash is usually the most significant line in the stack trace.

2160

An example of the list of crashes in the crashes page of the Instabug dashboard.

Next to each crash in the list, you can find the following details, all of which can be used to sort the crashes:

  • Occurrences: The number of times this crash has occurred and a bar graph representing its occurrences over the past seven days.
  • Users: The number of users affected by this crash.
  • Min ver.: The oldest app version that was affected by this crash.
  • Max ver.: The latest app version that was affected by this crash.
  • Last seen: The last time an occurrence of this crash was reported.

You can then filter for crashes that match any of the following criteria:

  • App version
  • Date
  • Device
  • OS
  • User attributes
  • Type
  • Status
  • Assignee
  • Team
  • Tags
  • Current view
  • Experiments

What’s Next

Learn more about the content contained in crash reports as well as how to deobfuscate them. You can also use callbacks to collect additional information.