Tag Archives: Performance Optimization

Accurately Measure Android App Performance with Profileable Builds


Posted by Yi Yang (Software Engineer)
It’s important to stay on top of your app performance to make sure your users can easily use your app. When an app experiences issues such as animation jank, frozen frames, and high memory usage, it negatively impacts the user experience which could lead to lower ratings or app deletion. To fix these performance issues, we first need the right tools to measure app performance correctly.
This is where profiling comes in. Profiling helps you find where CPU cycles and memory are spent at the time of inspection which makes it easier for you to pinpoint performance bottlenecks in your app. Android Studio offers a suite of profilers to help with the inspection.
Screenshot of Android Studio profilers
Historically, profiling an Android app required a debug build.
Screenshot of the debug/release build variants in Android Studio

The debug build allows you to use features useful for development, like Apply Changes, working with the debugger, or the Database Inspector. In addition, it also enables profiling tools to inspect the state of a running app unavailable to the release build.

Under the hood, the debug build sets the debuggable flag in the Android Manifest to true.

AndroidManifest.xml


<application android:debuggable="true">

  ...

</application>


While useful, the debug build is meant to provide more information at the cost of performance. That’s because when debuggable is true, a lot of compiler optimizations are turned off.
Screenshot of the Profile HWUI rendering setting in Developer Options. The option is in Developer Options > Monitoring > Profile HWUI rendering > On screen as bars
To show you the performance difference between the debug and release builds, we recorded an app running on the same device but in these two build variants. To visualize the frame rendering time, we turned on Profile GPU Rendering (or Profile HWUI rendering in some Android versions) in Developer Options when recording the screen. Each vertical bar on the bottom of the screen represents how long each frame takes to render. The shorter these bars are, the smoother the animation is.

The screen recording below shows the same app running on the same device. The left-hand side is on a debug build, the right-hand side a release build. The debug version has more stuttering frames, also known as UI jank. This means when you profile the debug build, you may see timing measurements significantly different from what your users see in the release build, and you may end up optimizing something that is not the problem.
GIF showing the performance difference between debug and release builds












To address that issue, the Android platform introduced a tag called profileable. It enables many profiling tools that measure timing information, without the performance overhead of the debug build. Profileable is available on devices running Android 10 or higher.

AndroidManifest.xml

<application>
    <profileable android:shell=["true" | "false"] />
</application>

Let’s look at another screen recording. This time, the left side shows a profileable release app and the right side an unmodified release app. There’s little performance difference between the two.

GIF showing the performance difference between profileable and release builds


With profileable, you can now measure the timing information much more accurately than the debug build.

This feature is designed to be used in production where app security is paramount. Therefore we decided to only support profiling features such as Callstack Sampling and System Trace, where timing measurement is critical. The Memory Profiler only supports Native Memory Profiling. The Energy Profiler and Event Timeline are not available. The complete list of disabled features can be found here. All these restrictions are put in place to keep your app's data safe.

Now that you know what the profileable tag does, let me show you how to use it. There are two options: automatically and manually.


Option 1: Use the option in Android Studio.

With Android Studio Flamingo and Android Gradle Plugin 8.0, all you need to do is just select this option from the Profile dropdown menu in the Run toolbar: “Profile with low overhead”. Then Android Studio will automatically build a profileable app of your current build type and attach the profiler. It works for any build type, but we highly recommend you to profile a release build, which is what your users see.

Screenshot of the one-click profileable builds feature in Android Studio Flamingo Canary
When a profileable app is being profiled, there is a visual indicator along with a banner message. Only the CPU and Memory profilers are available.
Screenshot of Android Studio profiler profiling a profileable build
In the Memory Profiler, only the native allocation recording feature is available due to security reasons.
Screenshot showing Android Studio memory profiler features when profiling a profileable build











This feature is great for simplifying the process of local profiling but it only applies when you profile with Android Studio. Therefore, it can still be beneficial to manually configure your app in case you want to diagnose performance issues in production or if you’re not ready to use the latest version of Android Studio or Android Gradle plugin yet.


Option 2: Manual configuration.

It takes 4 steps to manually enable profileable.

1.    Add this line to your AndroidManifest.xml.

 AndroidManifest.xml

<application>
  <profileable android:shell="true" />
</application>

2.    Switch to the release build type (or any build type that’s not debuggable).
Screenshot of selecting the active build variant in Android Studio
















3.    Make sure you have a signing key configured. To prevent compromising your release signing key, you can temporarily use your debug signing key, or configure a new key just for profiling.




 








 




  

 
4.    Build and run the app on a device running Android 10 or higher. You now have a profileable app. You can then attach the Android Studio profiler by launching the Profiler tool window and selecting the app process from the dropdown list.
Screenshot of process selection in Android Studio profilersMany of you may wonder if it is safe to leave the profileable manifest tag in production and the answer is yes. This tag is designed to be usable in release builds to enable local profiling. No memory data is readable by the host profiling tools and the shell process. Only stack traces are readable, which are typically obfuscated or lacking symbols in release builds.

In fact, many first-party Google apps such as Google Maps ship their app to the Play Store as profileable apps.

Screenshot showing Google Maps as a profileable process in the profiler process dropdownIn summary, profiling the debug build may skew the performance and therefore it’s better to analyze the release build with the profileable tag enabled.

Here’s a table that shows which build type should be used:

ReleaseProfileable ReleaseDebug
ProductionProfiling CPU timingDebugger, Inspectors, etc.

Profiling memory, energy, etc.

To learn more about profilable builds, start by reading the documentation and the the user guide.

With these tools provided by the Android team, we hope you can make your app run faster and smoother.