A mystery of slow rendering in one Android app

Preface

Calculator++ is a calculator app on Android that I started more than 6 years ago. It evolved with time: project structure changed, new libraries came, Gradle substituted Maven and Dagger — ServiceLocator. One thing, however, didn’t change — from the beginning I wanted to achieve a perfect user experience. My vision is that the app should always start instantly and it shouldn’t lag or freeze when user interacts with it.

Every metric I used to check performance looked fine until Android Vitals started to show a “Slow rendering” warning. And here the mystery begins.

First steps

I started looking at the stats to figure out what exactly was wrong.

The distribution of the render time looked a bit weird but it didn’t catch my eye in the beginning.

The weirdness is caused by the fact that the distribution is not normal (Gaussian)— it has two peaks and seems more like two normally distributed values combined together.

It was also quite strange to see “Slow draw command” to be that high (83%) as usually it’s “Slow UI thread” that slows down the rendering.

Furthermore, device and Android version distribution revealed that only Android 7+ devices were affected with Nexus 5X and Nexus 6P top high in the list.

Basically, only high-end Android 7+ devices were affected. Strange? Indeed. Especially, given that these devices are not the most popular devices among the users of the app.

Investigation

Luckily I had a Pixel phone that I could use for debugging.

First thing I did was to enable GPU monitor in Developer Options: Profile GPU rendering — On screen as bars. This option gives you a quick visual representation of how much time it takes to render the frames of a UI window relative to the 16-ms-per-frame benchmark.

There is plenty of information about how to analyze GPU rendering in Android. See, for example, this and this articles on d.android.com

As you can see from the video, almost all the frames were rendered under 16ms (green line). For most of them, it took even less than 8ms. The only long frames I could find happened during an activity creation when a view hierarchy was inflated. But they couldn’t contribute much to the slow rendering issue as there were too few of them.

After looking at the stats in Google Play Console again I started to suspect some secondary screen to cause the problem as the distribution looked quite suspicious to me. But I couldn’t neither find such screen nor could I get the proof on Google Analytics — secondary screens were used too seldom in the app and couldn’t cause much of a trouble.

After spending a couple of hours of reading the code and trying to figure out the cause I gave up. But when I was about to unplug the phone from my laptop I noticed some strange activity on the screen: GPU profiler went wild and almost every rendered frame exceeded 16ms limit! wat?! Slow rendering with nothing happening on the screen?

It started to become more interesting. GPU monitor built in Android Studio revealed that the rendering happened in pulses, each pulse corresponding to a cursor blink in the edit area. It indeed showed that it was swapping the buffers and issuing drawing commands that took too much time to execute.

I even tried the default calculator app and got similar results. And that was an indication for me that there is nothing wrong in my app but something on a system level didn’t work well.

My guess is that Android OS goes in some energy-saving mode that causes even trivial operations to take a lot of time. I couldn’t find any better solution than disabling the blinking cursor after some time of user inactivity. I didn’t implement this workaround in the app as it was quite hard to detect when a user stops interacting with the UI and, hey, this “slow rendering” issue doesn’t cause any harm :)

***

I started writing this article in August 2017 but managed to publish it only now (February 2018). However, not much has changed in Google Play Console since then — I still get slow rendering issue in Calculator++. But now I know that I can’t do much about it and that it doesn’t affect user experience in any way.