Leveling Guide for your Performance Journey

Posted by Alice Yuan - Senior Developer Relations Engineer


Welcome to day 4 of Performance Spotlight Week. Now that you've learned about some of the awesome tools and best practices we've introduced recently such as the R8 Optimizer, and Profile Guided Optimization with Baseline Profiles and Startup Profiles, you might be wondering where to start your performance improvement journey. 

We've come up with a step-by-step performance leveling guide to meet your mobile development team where you are—whether you're an app with a single developer looking to get started with performance, or you have an entire team dedicated to improving Android performance. 

The performance leveling guide features 5 levels. We'll start with level 1, which introduces minimal adoption effort performance tooling, and we'll go up to level 5, ideal for apps that have the resourcing to maintain a bespoke performance framework.

 
Feel free to jump to the level that resonates most with you:

Level 1:  Use Play Console provided field monitoring

We recommend first leveraging Android vitals within the Play Console for viewing automatically collected field monitoring data, giving you insights about your application with minimal effort.

Android vitals is Google's initiative to automatically collect and surface this field data for you.

Here's an explanation of how we deliver this data:

  1. Collect Data: When a user opts-in, their Android device automatically logs key performance and stability events from all apps, including yours.

  2. Aggregate Data: Google Play collects and anonymizes this data from your app's users.

  3. Surface Insights: The data is presented to you in the Android vitals dashboard within your Google Play Console.

The Android vitals dashboard tracks many metrics, but a few are designated as Core Vitals. These are the most important because they can affect your app's visibility and ranking on the Google Play Store.

The Core Vitals

GOOGLE PLAY'S CORE TECHNICAL QUALITY METRICS

To maximize visibility on Google Play, keep your app below the bad behavior thresholds for these metrics.

User-perceived crash rate The percentage of daily active users who experienced at least one crash that is likely to have been noticeable
User-perceived ANR rate The percentage of daily active users who experienced at least one ANR that is likely to have been noticeable
Excessive battery usage The percentage of watch face sessions where battery usage exceeds 4.44% per hour
New: Excessive partial wake locks The percentage of user sessions where cumulative, non-exempt wake lock usage exceeds 2 hours

The core vitals include user-perceived crash rate, ANR rate, excessive battery usage and the newly introduced metric on excessive partial wake locks.

User-Perceived ANR Rate

You can use the Android Vitals ANR dashboard, to see stack traces of issues that occur in the field and get insights and recommendations on how to fix the issue. 

You can drill down into a specific ANR that occurred, to see the stack trace as well as insights on what might be causing the issue.

Also, check out our ANR guidance to help you diagnose and fix the common scenarios where ANRs might occur. 

User-Perceived Crash Rate 

Use the Android vitals crflevelash dashboard to further debug crashes and view a sample of stack traces that occur within your app. 


Our documentation also has guidance around troubleshooting specific crashes. For example, the Troubleshoot foreground services guide discusses ways to identify and fix common scenarios where crashes occur.

Excessive Battery Usage 

To decrease watch face sessions with excessive battery usage on Wear OS, check out the Wear guide on how to improve and conserve battery

[new] Excessive Partial Wake Locks


We recently announced that apps that exceed the excessive partial wake locks threshold may see additional treatment starting on March 1st 2026

For mobile devices, the Android vitals metric applies to non-exempted wake locks acquired while the screen is off and the app is in the background or running a foreground service. Android vitals considers partial wake lock usage excessive if wake locks are held for at least two hours within a 24-hour period and it affects more than 5% of your app's sessions, averaged over 28 days.

To debug and fix excessive wake lock issues, check out our technical blog post.

Consult our Android vitals documentation and continue your journey to better leverage Android vitals.

Level 2: Follow the App Performance Score action items

Next, move onto using the App Performance Score to find the high leverage action items to uplevel your app performance.

The Android App Performance Score is a standardized framework to measure your app's technical performance. It gives you a score between 0 and 100, where a lower number indicates more room for improvement.

To get easy wins, you should first start with the Static Performance Score first. These are often configuration changes or tooling updates that provide significant performance boosts.

Step 1: Perform the Static Assessment

The static assessment evaluates your project's configuration and tooling adoption. These are often the quickest ways to improve performance.

Navigate to the Static Score section of the scoreboard page and do the following:

  1. Assess Android Gradle Plugin (AGP) Version.

  2. Adopt R8 Minification incrementally or ideally, use R8 in full mode to minify and optimize the app code.

  3. Adopt Baseline Profiles which improves code execution speed from the first launch providing performance enhancements for every new app install and every app update.

  4. Adopt Startup Profiles to improve Dex Layout. Startup Profiles are used by the build system to further optimize the classes and methods they contain by improving the layout of code in your APK's DEX files. 

  5. Upgrade to the newest version of Jetpack Compose

Step 2: Perform the Dynamic Assessment

Once you have applied the static easy wins, use the dynamic assessment to validate the improvements on a real device. You can first do this manually with a physical device and a stop watch.

Navigate to the Dynamic Score section of the scoreboard page and do the following:

  1. Set up your test environment with a physical device. Consider using a lower-end device to exaggerate performance issues, making them easier to spot.

  2. Measure startup time from the launcher. Cold start your app from the launcher icon and measure the time until it is interactive.

  3. Measure app startup time from a notification, with the goal to reduce notification startup time to be below a couple seconds.

  4. Measure rendering performance by scrolling through your core screens and animations.

Once you've completed these steps, you will receive a score between 1 - 100 for the static and dynamic scores, giving you an understanding of your app's performance and where to focus on.

Level 3: Leverage local performance test frameworks


Once you've started to assess dynamic performance, you may find it too tedious to measure performance manually. Consider automating your performance testing using performance test frameworks such as Macrobenchmarks and UiAutomator.

Macrobenchmark 💚 UiAutomator

Think of Macrobenchmark and UiAutomator as two tools that work together: Macrobenchmark is the measurement tool. It's like a stopwatch and a frame-rate counter that runs outside your app. It is responsible for starting your app, recording metrics (like startup time or dropped frames), and stopping the app. UiAutomator is the robot user. The library lets you write code to interact with the device's screen. It can find an icon, tap a button,  scroll on a list and more.

How to write a test

When you write a test, you wrap your UiAutomator code inside a Macrobenchmark block.

  1. Define the Test: Use the @MacrobenchmarkRule

  2. Start Measuring: Call benchmarkRule.measureRepeated.

  3. Drive the UI: Inside that block, use UiAutomator code to launch your app, find UI elements, and interact with them.

Here's an example code snippet of what it looks like to test a compose list for scrolling jank.


benchmarkRule.measureRepeated(

    // ...

    metrics = listOf(

        FrameTimingMetric(),

    ),

    startupMode = StartupMode.COLD,

    iterations = 10,

) {

    // 1. Launch the app's main activity

    startApp()

    // 2. Find the list using its resource ID and scroll down

    onElement { viewIdResourceName == "$packageName.my_list" }

        .fling(Direction.DOWN)

}


  1. Review the results: Each test run provides you with precisely measured information to give you the best data on your app's performance.

timeToInitialDisplayMs  min  1894.4,   median 2847.4,   max  3355.6


frameOverrunMs          P50 -3.2,  P90  6.2, P95  10.4, P99  119.5

Common use cases

Macrobenchmark provides several core metrics out of the box. StartupTimingMetric allows you to accurately measure app startup. The FrameTimingMetric enables you to understand an app's rendering performance during the test.

We have a detailed and complete guide to using Macrobenchmarks and UiAutomator alongside code samples available for you to continue learning.

Level 4: Use trace analysis tools like Perfetto 

Trace analysis tools like Perfetto are used when you need to see beyond your own application code. Unlike standard debuggers or profilers that only see your process, Perfetto captures the entire device state—kernel scheduling, CPU frequency, other processes, and system services—giving you complete context for performance issues.

Check our Performance Debugging youtube playlist for video instructions on performance debugging using system traces, Android Studio Profiler and Perfetto.

How to use Perfetto to debug performance

The general workflow for debugging performance using trace analysis tools is to record, load and analyze the trace. 

Step 1: Record a trace

You can record a system trace using several methods: 

Step 2: Load the trace

Once you have the trace file, you need to load it into the analysis tool.

  1. Open Chrome and navigate to ui.perfetto.dev.

  2. Drag and drop your .perfetto-trace (or .pftrace) file directly into the browser window.

  3. The UI will process the file and display the timeline.

Step 3: Analyze the trace


You can use Perfetto UI or Android Studio Profiler to investigate performance issues. Check out this episode of the MAD Skills series on Performance, where our performance engineer Carmen Jackson discusses the Perfetto traceviewer.

Scenarios for inspecting system traces using Perfetto

Perfetto is an expert tool and can provide information about everything that happened on the Android device while a trace was captured. This is particularly helpful when you cannot identify the root cause of a slowdown using standard logs or basic profilers.

Debugging Jank (Dropped Frames)

If your app stutters while scrolling, Perfetto can show you exactly why a specific frame missed its deadline.

If it’s due to the app, you might see your main thread running for a long duration doing heavy parsing; this indicates scenarios where you should move the work into asynchronous processing.

If it’s due to the system, you might see your main thread ready to run, but the CPU kernel scheduler gave priority to a different system service, leaving your app waiting (CPU contention). This indicates scenarios where you may need to optimize usage of platform APIs.

Analyzing Slow App Startup

Startup is complex, involving system init, process forking, and resource loading. Perfetto visualizes this timeline precisely.

You can see if you are waiting on Binder calls (inter-process communication). If your onCreate waits a long time for a response from the system PackageManager, Perfetto will show that blocked state clearly. 

You can also see if your app is doing more work than necessary during the app startup. For example, if you are creating and laying out more views than the app needs to show, you can see these operations in the trace.

Investigating Battery Drain & CPU Usage

Because Perfetto sees the whole system, it's perfect for finding invisible power drains.

You can identify which processes are holding wake locks, preventing the device from sleeping under the “Device State” tracks. Learn more in our wake locks blog post. Also, use Perfetto to see if your background jobs are running too frequently or waking up the CPU unnecessarily.

Level 5: Build your own performance tracking framework

The final level is for apps that have teams with resourcing to maintain a performance tracking framework. 

Building a custom performance tracking framework on Android involves leveraging several system APIs to capture data throughout the application lifecycle, from startup to exit, and during specific high-load scenarios.

By using ApplicationStartInfo, ProfilingManager, and ApplicationExitInfo, you can create a robust telemetry system that reports on how your app started, detailed info on what it did while running, and why it died.

ApplicationStartInfo: Tracking how the app started

Available from Android 15 (API 35), ApplicationStartInfo provides detailed metrics about app startup in the field. The data includes whether it was a cold, warm, or hot start, and the duration of different startup phases. 

This helps you develop a baseline startup metric using production data to further optimize that might be hard to reproduce locally. You can use these metrics to run A/B tests optimizing the startup flow.

The goal is to accurately record launch metrics without manually instrumenting every initialization phase.

You can query this data lazily some time after application launch.

ProfilingManager: Capturing why it was slow

ProfilingManager (API 35) allows your app to programmatically trigger system traces on user devices. This is powerful for catching transient performance issues in the wild that you can't reproduce locally.

The goal is to automatically record a trace when a specific highly critical user journey is detected as running slowly or experiencing performance issues.

You can register a listener that triggers when specific conditions are met or trigger it manually when you detect a performance issue such as jank, excessive memory, or battery drain.

Check our documentation on how to capture a profile, retrieve and analyze profiling data and use debug commands.

ApplicationExitInfo: Tracking why the app died

ApplicationExitInfo (API 30) tells you why your previous process died. This is crucial for finding native crashes, ANRs, or system kills due to excessive memory usage (OOM). You'll also be able to get a detailed tombstone trace by using the API getTraceInputStream.

The goal of the API is to understand stability issues that don't trigger standard Java crash reporters (like Low Memory Kills).

You should trigger this API on the next app launch.

Next Steps

Improving Android performance is a step-by-step journey. We're so excited to see how you level up your performance using these tools!

Tune in tomorrow for Ask Android

You have shrunk your app with R8 and optimized your runtime with Profile Guided Optimization. And measure your app's performance.

Join us tomorrow for the live Ask Android session. Ask your questions now using #AskAndroid and get them answered by the experts.



Android Quick Share Support for AirDrop: A Secure Approach to Cross-Platform File Sharing

Technology should bring people closer together, not create walls. Being able to communicate and connect with friends and family should be easy regardless of the phone they use. That’s why Android has been building experiences that help you stay connected across platforms.

As part of our efforts to continue to make cross-platform communication more seamless for users, we've made Quick Share interoperable with AirDrop, allowing for two-way file sharing between Android and iOS devices, starting with the Pixel 10 Family. This new feature makes it possible to quickly share your photos, videos, and files with people you choose to communicate with, without worrying about the kind of phone they use.

Most importantly, when you share personal files and content, you need to trust that it stays secure. You can share across devices with confidence knowing we built this feature with security at its core, protecting your data with strong safeguards that have been tested by independent security experts.

Secure by Design

We built Quick Share’s interoperability support for AirDrop with the same rigorous security standards that we apply to all Google products. Our approach to security is proactive and deeply integrated into every stage of the development process. This includes:

  • Threat Modeling: We identify and address potential security risks before they can become a problem.
  • Internal Security Design and Privacy Reviews: Our dedicated security and privacy teams thoroughly review the design to ensure it meets our high standards.
  • Internal Penetration Testing: We conduct extensive in-house testing to identify and fix vulnerabilities.

This Secure by Design philosophy ensures that all of our products are not just functional but also fundamentally secure.

This feature is also protected by a multi-layered security approach to ensure a safe sharing experience from end-to-end, regardless of what platform you’re on.

  • Secure Sharing Channel: The communication channel itself is hardened by our use of Rust to develop this feature. This memory-safe language is the industry benchmark for building secure systems and provides confidence that the connection is protected against buffer overflow attacks and other common vulnerabilities.
  • Built-in Platform Protections: This feature is strengthened by the robust built-in security of both Android and iOS. On Android, security is built in at every layer. Our deep investment in Rust at the OS level hardens the foundation, while proactive defenses like Google Play Protect work to keep your device safe. This is complemented by the security architecture of iOS that provides its own strong safeguards that mitigate malicious files and exploitation. These overlapping protections on both platforms work in concert with the secure connection to provide comprehensive safety for your data when you share or receive.
  • You’re in Control: Sharing across platforms works just like you're used to: a file requires your approval before being received, so you're in control of what you accept.

The Power of Rust: A Foundation of Secure Communication

A key element of our security strategy for the interoperability layer between Quick Share and AirDrop is the use of the memory-safe Rust programming language. Recognized by security agencies around the world, including the NSA and CISA, Rust is widely considered the industry benchmark for building secure systems because it eliminates entire classes of memory-safety vulnerabilities by design.

Rust is already a cornerstone of our broader initiative to eliminate memory safety bugs across Android. Its selection for this feature was deliberate, driven by the unique security challenges of cross-platform communication that demanded the most robust protections for memory safety.

The core of this feature involves receiving and parsing data sent over a wireless protocol from another device. Historically, when using a memory-unsafe language, bugs in data parsing logic are one of the most common sources of high-severity security vulnerabilities. A malformed data packet sent to a parser written in a memory-unsafe language can lead to buffer overflows and other memory corruption bugs, creating an opportunity for code execution.

This is precisely where Rust provides a robust defense. Its compiler enforces strict ownership and borrowing rules at compile time, which guarantees memory safety. Rust removes entire classes of memory-related bugs. This means our implementation is inherently resilient against attackers attempting to use maliciously crafted data packets to exploit memory errors.

Secure Sharing Using AirDrop's "Everyone" Mode

To ensure a seamless experience for both Android and iOS users, Quick Share currently works with AirDrop's "Everyone for 10 minutes" mode. This feature does not use a workaround; the connection is direct and peer-to-peer, meaning your data is never routed through a server, shared content is never logged, and no extra data is shared. As with "Everyone for 10 minutes" mode on any device when you’re sharing between non-contacts, you can ensure you're sharing with the right person by confirming their device name on your screen with them in person.

This implementation using "Everyone for 10 minutes” mode is just the first step in seamless cross-platform sharing, and we welcome the opportunity to work with Apple to enable “Contacts Only” mode in the future.

Tested by Independent Security Experts

After conducting our own secure product development, internal threat modeling, privacy reviews, and red team penetration tests, we engaged with NetSPI, a leading third-party penetration testing firm, to further validate the security of this feature and conduct an independent security assessment. The assessment found the interoperability between Quick Share and AirDrop is secure, is “notably stronger” than other industry implementations and does not leak any information.

Based on these internal and external assessments, we believe our implementation provides a strong security foundation for cross-platform file sharing for both Android and iOS users. We will continue to evaluate and enhance the implementation’s security in collaboration with additional third-party partners.

To complement this deep technical audit, we also sought expert third-party perspective on our approach from Dan Boneh, a renowned security expert and professor at Stanford University:

“Google’s work on this feature, including the use of memory safe Rust for the core communications layer, is a strong example of how to build secure interoperability, ensuring that cross-platform information sharing remains safe. I applaud the effort to open more secure information sharing between platforms and encourage Google and Apple to work together more on this."

The Future of File-Sharing Should Be Interoperable

This is just the first step as we work to improve the experience and expand it to more devices. We look forward to continuing to work with industry partners to make connecting and communicating across platforms a secure, seamless experience for all users.

Chrome Beta for Android Update

Hi everyone! We've just released Chrome Beta 143 (143.0.7499.34) for Android. It's now available on Google Play.

You can see a partial list of the changes in the Git log. For details on new features, check out the Chromium blog, and for details on web platform updates, check here.

If you find a new issue, please let us know by filing a bug.

Chrome Release Team
Google Chrome