This article originally appeared on

How to fix app quality issues with Android vitals (Part 2)

Post 2 of 2: How fixing crashes and unnecessary wake locks can improve Play Store performance

Wojtek Kaliciński
Google Play Apps & Games


I’m excited that more and more of you are finding success fixing app quality issues using the Android vitals section in the Google Play Console. Since my first article about Android vitals, we’ve been making improvements to bring you new metrics and features. In this article, I’ll start with a look at what’s new, then take you through how to work with the stuck wake locks and crashes.

What’s new in Android vitals

We announced new Android vital features at Google I/O 2018. These improvements included two new performance areas, enhancements to existing metric information, and a way to alert you to unexpected changes in your Android vitals.

Category benchmarks

The measure of what constitutes a “good” (low) or “bad” (high) level of vitals in an app depends a lot on the Play Store category the app is in. For example, apps in the “Board games” category have, on average, a lower startup latency than those apps categorized under “Racing games”. That’s why Android vitals now shows category benchmarks, so you can see how your app is performing relative to other apps in the same category.

Anomaly detection

A sudden change in your app’s behavior is not a good thing. To help you get on top of these situations quickly, whenever Android vitals detects a sudden spike in a vital, it will alert you in the Play Console, and by email. You can then visit Android vitals to see what’s happening and address the cause.

Permission denials

See the percentage of people who denied a permission request made by your app. Where the level of denials is high you can review your app design to see if, for example, better highlighting of the reason for the permission will help people be more confident about granting it.

Application startup time

Even if you’re delivering great functionality or an outstanding gameplay experience, people won’t hang around to use it if your app starts too slowly. To help you understand if this could be a challenge in your app, this new performance area shows how quickly your app or game starts from the three types of app start: cold start, when your app hasn’t been used recently and isn’t cached in memory; warm start, when your app is in memory, but the Activity needs to be recreated; and hot start, when the app and Activity are in memory when the user navigated back to the app.

When you see issues with start-up in Android vitals, use the new Android Profiler tools in Android Studio 3.2, such as app start CPU profiling and Systrace, to track down startup latency problems.

Core vitals

All vitals are important for high-quality apps. But, there are four metrics that can be your priority: excessive wakeups, ANRs, stuck background wake locks, and crashes. Excessive wakeups and ANRs were the subject of my first article. So, now I’m going to look at wake locks and crashes.

Wake locks

Historically, doing background work in Android relied on creating background services as they were needed, which were run for as long as needed — sometimes called free-running services. Wake locks were used while running these background services to keep the device’s CPU and radios from powering down.

Using wake locks in this way is not optimal. Apps can schedule their services too often and end up running for too long taking up resources, such as memory. This use of resources often leads to a degradation in device performance and excessive battery drain, because of the increased CPU and radio use, and the fact that they prevent the device from sleeping.

Android vitals provides you with details of partial wake locks, acquired by your app through the PowerManager class, on the Stuck wake locks and Stuck wake locks (background) pages. A partial wake lock ensures the CPU is running but the screen and keyboard backlight can turn off.

On the reports, you see the percentage of battery sessions (the period between two full charges of the device) affected by stuck wake locks. The information includes the percentage of affected sessions over the previous two 30-day periods, benchmarks showing how your app performs against the top 1000 apps on Google Play (by number of installs), a graph of affected sessions over time, and (not shown in the screenshot) a series of breakdowns by criteria such as app version, Android OS version, and device.

If the benchmarks show your app’s portion of sessions affected by stuck wake locks is in the bottom 25%, start thinking about improving your app.

As mentioned in my first article, since the introduction of JobScheduler, in Android 5.0, the best practice advice has been to move away from using alarms and free running services. Starting with Android Oreo this move becomes mandatory, as your app can no longer start a service in the background (it will result in an exception). Similarly, move your apps away from using wake locks, as there are now better alternatives for most use cases. So, rather than using wake locks, we recommend the following for your app:

  • Use JobScheduler or WorkManager API when a job needs to run in the background. Using these features will hold a wake lock for the job while it is running.
  • Schedule an AlarmManager broadcast when you want to run a background task at a fixed time or regular intervals. AlarmManager holds a wake lock for as long as your BroadcastReceiver.onReceive() method is running.
  • Add FLAG_KEEP_SCREEN_ON to any Window where you want to keep the screen on while someone is looking at an Activity in your app. For example, when reading an ebook. With this method, the system will clean up the wake lock when the Activity is no longer visible.

When you need to manage a wake lock yourself (for example, when using a long-running foreground service to play an audio track), remember to follow these rules:

  • Use PARTIAL_WAKE_LOCK, don’t use the deprecated types of wake locks.
  • Specify a timeout when you acquire a wake lock, that way if your app doesn’t release the wake lock the system will clean it up when the timeout expires.
  • Give wake locks a static, descriptive tag, such as com.myapp: my wake lock to get the best reporting of wake locks in Android vitals. Don’t add counters or other dynamic data to the tag. With good descriptive tags, Android vitals clusters (combines) similar wake locks to give you an accurate picture of the behavior of individual wake locks.
  • Release wake locks when they are no longer needed. Remember that errors can also render the wake lock unneeded. A good approach to handling errors is to code defensively and wrap calls in a try { … } finally { wakeLock.release(); } block.


Android vitals provides both summary and detail views of crashes. On the Crash rate and Multi-crash rate pages, you see crash data combined with use data to create a normalized metric.

In addition to this summary information, you can also see real-time crash details on the ANRs & Crashes page.

As with stuck background wake locks, the key metric to compare your crash rate with is the bad behavior threshold. If your app’s crash rate exceeds the bad behavior threshold, we recommend that you address crashes as a priority.

There are many reasons why your code can produce an exception and crash your app. It’s, therefore, impractical to discuss and offer a solution to them all here. I can, however, provide some general advice to help avoid crashes.

Android vitals is a great way to get information about the most common crashes and how many people are affected by them. A big advantage to Android vitals is that it captures crashes that happen before third-party crash reporting SDKs have a chance to initialize.

If you want more information to help with reproducing, debugging, and fixing crashes, consider using Firebase Crashlytics. This tool provides insights into what’s happening in your app at the time of a crash. Firebase Crashlytics does this by integrating with Firebase Analytics and enabling you to add custom logging to include with the crash reports.

My key advice for avoiding many of the common causes of crashes is:

  • Don’t reinvent
  • Switch to Kotlin
  • Stick to the public APIs

Don’t reinvent common code patterns

Why run the risk of making mistakes coding for a common task, when high-quality libraries that are unlikely to cause a crash are readily available and easy to reuse.

A good source of libraries for many of the things you want to do in your code is Android Jetpack, a set of libraries that can serve as a foundation for most Android apps. You can rely on Android Jetpack for many common tasks, such as activity lifecycle management, navigation, running background jobs, database operations, loading data from external sources, and more. You don’t have to limit yourself to libraries developed by Google. There are many great, tested, and open source libraries available that you can use in your apps.

Consider switching from Java to Kotlin

Last year at I/O we announced that Android supported Kotlin. We’re continuing that investment with the improvements to Kotlin support. The ultimate goal is to make developers more productive, at least in part by avoiding some common coding errors that might lead to crashes.

One great thing about Kotlin is that it has nullability information built into the language’s type system. With null-safe calls and compile-time null checks, you can be more confident that your code will not cause a NullPointerException at runtime. I encourage you to try Kotlin if you haven’t already. You can start adding it to your app slowly, there is no need to replace all your Java code as Kotlin offers great interoperability with Java.

Stick to using the public Android APIs

The public Android APIs are the classes and public methods documented on the Android Developer site. These APIs are guaranteed to be available and work on all certified Android devices. The private or hidden methods, that can be accessed using reflection, can be a big source of crashes. The challenge with these methods is that they aren’t guaranteed to be available on devices and often change or break with each Android update. So, they are fine for experimenting but we would recommend avoiding them in public Play Store apps.

Because of the issues with private or hidden methods, in the Android P Developer Preview and for all future editions of Android access to these undocumented, private APIs is being restricted and your apps may stop using them immediately to remain compatible. If you want to learn more, check out the preview site article ‘Restrictions on non-SDK interfaces’.

A final word

Android vitals is here to help you build better quality apps that engage and retain more uses so you can build vibrant business. Over my two articles I gave you essential information about the four core vitals: excessive wakeups, ANRs, stuck background wake locks and crashes. I’ve also looked at the features recently added to Android vitals.

Hopefully by following the recommendations and insights in this article, it will help you make better sense of your apps’ Android vitals and use these to improve the quality of your app.

If you want to learn more, check out the recording of Android vitals: debug app performance and reap rewards a Google I/O 2018 session I co-presented with Joel t Newman and Fergus Hurley. You can also learn more best practices for Android apps and games from Google Play on the Medium blog.

What do you think?

If you have thoughts on Android vitals or any of its new features, let us know by tweeting using #AskPlayDev and we’ll reply from @GooglePlayDev, where we regularly share news and tips on how to be successful on Google Play.