Understanding how stable your Android app is can be challenging once you have released it. An important part of stability is understanding all the different ways that your app process ends. The operating system has a great degree of control in how an app process can be terminated, so it is not possible to get a complete picture of all types of process terminations from within an app.
So, what do you do? Well, for older Android versions your options are limited, but Android 11 introduced the ApplicationExitInfo API, which gives you insight into your app’s most recent process terminations. This API also provides info about Application Not Responding (ANR) errors, similar to what you will see for your app in the Google Play Console, even if the ANR did not directly lead to the process termination.
In this post, we’ll provide an overview of the ApplicationExitInfo API, how to use it, what its strengths and weaknesses are, and how you can best leverage this data to improve your mobile user experiences.
What is the ApplicationExitInfo API?
You can get a list of reasons for the most recent process terminations for your app using android.app.ActivityManager’s getHistoricalProcessExitReasons() method. This will return a list of ApplicationExitInfo instances. The number of response values will depend on how many launches the app has gone through. Android stores the data in a circular buffer, so you will need to keep track of which ApplicationExitInfo objects you have already seen and which are new.
An ApplicationExitInfo object contains a number of interesting fields that will help you understand why your app is being terminated, and often provide a bit of context around this. Key pieces of information include the termination reason, a description of the termination, and the time the app was terminated.
In the case where the process was terminated for a mundane reason, such as when an app has been sent to the background and Android needs to reclaim memory for other apps, there is no additional information available. However, when the application exited due to a native crash or an ANR, additional data that is associated with the ApplicationExitInfo instance can be retrieved with its getTraceInputStream() method.
What can the ApplicationExitInfo API be used for?
There are 17 exit reasons defined at the time of writing, with most of them having existed since the API was introduced in Android 11 (API level 30). We will only cover the reasons that are most common and important for the majority of apps in this post:
- ANR — the app was terminated after an ANR occurred. If you are trying to troubleshoot ANRs that show up in Google Play Console, the trace data that accompanies many ANR exits can be helpful.
- Crash — the app crashed due to an unhandled Java exception. This exit reason does not have a trace associated with it. You still have to capture the unhandled exception as you normally would.
- Native crash — the app was terminated due to a crash in native code. Typically, trace data is available for this exit reason, which you may find provides an easier way to capture native crash data than configuring the signal handlers needed to capture this information.
- Excessive resource usage — the app was terminated because it tried to use more CPU resources than Android was willing or able to grant it.
These exit reasons are all key factors that impact the stability of your app.
What are the strengths of the ApplicationExitInfo API?
The biggest benefit of the ApplicationExitInfo API is that it provides you access to information that the OS collects, that you have no other practical way to access in a production environment. The ANR data, while not exactly the same as what powers the ANR part of the Google Play Console, is quite similar.
Since an app has no warning when it is killed for excessive resource usage, and thus nothing can be recorded from the context of the application at the time that this termination occurs, getting access to those exit reasons allows you to understand what otherwise would appear to be an unexplained app exit.
What are the weaknesses of the ApplicationExitInfo API?
While getting access to this data is a great step in the right direction for Android observability, figuring out how to leverage this data requires significant effort in most cases. Overall, this data should be considered to be fairly dirty and, in our experience, requires substantial effort to clean.
A few examples:
- Typically we see about 30% of trace dumps for ANRs are missing the stack traces for the threads. This limits the value of this information since you no longer have any information about why the ANR happened and just know that one happened.
- Description data can be confusing and incomplete. While the quality of this information improved in Android 12 and later, this data will often require you to look at the Android source code to provide more context. Android Code Search is your friend when diving into this.
- Many ApplicationExitInfo objects have the reason set to “other” which in most cases is not helpful in understanding why the exit occurred. Sometimes the description data will provide additional context, but that is not always the case.
There are also some patterns in the data that are probably not expected by most people upon first look. For example, ANR trace data may be associated with an ApplicationExitInfo instance even though that instance’s exit reason is not “ANR.” If an ANR occurred during the process lifetime, a trace will be captured and made available, regardless of what the actual process exit reason is.
It is also worth noting that the data is stored in a circular buffer, meaning there is a chance of data being overwritten before it is accessed. This can largely be mitigated by fetching the ApplicationExitInfo data on every app launch.
However, you are responsible for keeping track of the ApplicationExitInfo objects you’ve already seen to prevent data duplication, as Android doesn’t provide a built-in solution for this.
Finally, when accessing the full trace data, we have observed that sometimes reading the stream can take a fairly long time and it would be best to do that off the main thread.
How can I use the data from the ApplicationExitInfo API?
While you have access to the ApplicationExitInfo data in the app, most developers are likely to find the most value by sending the data to a service that can aggregate the data and show larger trends in the app’s user base, rather than using it to directly affect the end-user experience in the app.
In addition to the data cleanliness issues mentioned above, keep in mind when reading the trace data and sending it to a backend that they encapsulated the native crash data in Protocol Buffers (Protobufs), so they will contain binary data. Certain approaches to reading and storing this data may lead to unintended mangling of the binary data.
How does Embrace use the ApplicationExitInfo API?
Embrace uses the information from ApplicationExitInfo to get ANR data that aligns well with what is used to power the ANR component of the Google Play Console. We combine this ANR data with the other ANR data that our SDK collects to provide a more comprehensive picture of what caused the ANRs in your application than what is possible when only using a single source of ANR data.
We are in the process of incorporating more of the other exit reasons in our data pipeline.
The ApplicationExitInfo API is a very helpful additional source of information that informs you about the stability of your application and shines a light on the issues causing some of your stability issues. While it is a great source of information, the effort to leverage it reliably can be quite significant.
Learn how Embrace makes it easy to analyze and resolve Google Play Console ANRs by leveraging Google’s ApplicationExitInfo API, here.