An ANR Story - How not to freeze when your app is freezing

Akash Khunt
ProAndroidDev
Published in
5 min readApr 20, 2023

--

DALL·E generated image (an intergalactic frozen world)

In this post we’ll look at the methodology which we can use to categorize and tackle ANRs, as well as how it helped us reduce our User-perceived ANR rate by more than 60% 🤯 by an accident 😅. Since there is a lot of material available around “what is an ANR and how it occurs”, we won’t go into details of that.

Categorization

First step to understand/resolve any ANR it’s very important to categorize them into whether it’s User-perceived ANR or not. This information is readily available in Android Vitals on Play Console. As per the definition provided here, any ANR with Input dispatching timed out or Input dispatching timed out (No focused window) (occurs when an app fails to respond to an input event within 5 seconds) condition can be categorized as the User-perceived ANR ones.

One of the reason to focus on reducing your User-perceived ANR rate apart from bad User experience is that it’s also a core vital meaning that it affects the discoverability of your app on Google Play.

Input dispatching timed out (No focused window) type is not present in the Android Vitals documentation as a User-perceived ANR one, but it can be confirmed via clicking on View Issues in Android Vitals’s User-perceived ANR rate section. Refer following screenshot for reference.

Android Vitals Overview - User-perceived ANR rate
Android Vitals Crashes and ANRs - User-perceived ANR rate

In our case Input dispatching timed out (No focused window) type was the biggest culprit in User-perceived ANR rate, raising roughly 80% of the events. The issue here was that though since there is not any documentation on this ANR type we were not sure what exactly No focused window meant 😕.

The second biggest culprit in our overall ANR rate was Broadcast of intent type. Even though this is neither user-perceived nor a part of core vital, we wanted to resolve this as well to remove the noise added by it to the overall ANR rate. After looking at the ANR report in Play Console we found that Broadcast of intent type ANRs were happening mainly in the background (more than 99% of cases) and most of them seemed to be related to FCM.

Since Broadcast of intent ANR type is mainly background one, generic things like doing less work Main thread or doing I/O operation on background thread that we do to resolve mostly Foreground ANRs doesn’t help as these might be happening because of different reasons.

The reasoning behind Broadcast of intent ANR type can be understood via learning how the Android OS allocates resources during app startup when it’s being launched in Foreground vs Background. When app gets launched in foreground the OS tries to allocate maximum available resources to your app, which can be verified via checking the CPU activity (CPU will be running at ~100%) around the same time. But when your app launches in background then the OS will allocate very minimal amount of resources which might also depend on whether your device is being actively used or is in sleep mode.

From the above understanding as well after landing on an awesome Medium post published by the Bumble team we understood that if your app receives an FCM while not in RAM then before executing BroadcastReceiver.onReceive() your app has to execute Application.onCreate().

Armed with this learning we started profiling our Application.onCreate() and Content Provider execution time and we found few SDK initializations which were contributing to ~65% of the Application.onCreate() execution time. These SDK initializations were definitely needed during app startup but were not required to handle FCMs so we ended up moving it to out Activity.onCreate() in our single Activity architecture.

After we released an update with the above mentioned refactoring we were surprised by the results shown on Play Console. We expected the Broadcast of intent ANR type contribution to overall ANR rate go down drastically by 82% (numbers of Users affected went down from 10.9K to 1.6K) but to our surprise the Input dispatching timed out (No focused window) frequency also went down by staggering 99% (numbers of Users affected went down from 78.3K to 609) 🤯. From this we hypothesized that one of the root cause for Input dispatching timed out (No focused window) might be very long execution time of Application.onCreate() only.

You can check the before and after screenshots of few of the Input dispatching timed out (No focused window) and Broadcast of intent ANRs below. FYI the overall session count on both of them is very similar (~7% difference).

Before — Input dispatching timed out (No focused window) ANR type frequency
After - Input dispatching timed out (No focused window) ANR type frequency
Before — Broadcast of intent ANR type frequency
After — Broadcast of intent ANR type frequency

Hope this helps in reducing the User-perceived ANR rate for your app as well. 🙂

--

--