In order to improve the user experience of Jetpack Compose and learn more about its development and learning needs, we sincerely invite you to participate in the research of Jetpack Compose usage. Click here to participate in the research immediately.

By Andrew Flynn and Jon Boekenoogen, Google Play Tech Lead

In 2020, Google Play Store development team management made a big decision: to revamp the entire Play Store technology stack. Because the existing code is more than a decade old, there has been a huge technical liability over the course of numerous Android platform releases and feature updates. We needed a new framework that could support hundreds of engineers working simultaneously without compromising developer productivity, user experience, or Play Store performance itself.

We have a long-term roadmap for updating everything in the store from the web layer all the way down to pixel rendering. Along the way, we wanted to adopt a modern declarative interface framework to achieve our product goals around interactivity and user satisfaction. After reviewing our options, we made (at the time) a bold decision to use Jetpack Compose, which was still in the Alpha preview phase.

Since then, the Google Play Store has worked closely with the Jetpack Compose team to release and refine a version of Jetpack Compose that meets our specific needs. This article introduces you to our migration approach, the challenges and advantages we found along the way, and shares some insights into application selection Compose with many contributors.

Give priority to

When using Jetpack Compose for our new interface rendering layer, we need to consider the following two priorities:

  1. Developer productivity: The Play Store team has hundreds of engineers working on the code, so it should be easy (and fun) to develop.
  2. Performance: The Play Store renders a lot of media-intensive content, and many of the business metrics are sensitive to latency and lag, so we need to make sure it works well on all devices, especially low memory hardware and Android (Go version) devices.

Developer productivity

For over a year now, we’ve been writing user interface code using Jetpack Compose, which makes interface development much easier.

We tend to write interfaces with less code, sometimes as much as 50 percent less. This improvement is made possible by the fact that Compose is a declarative interface framework that takes advantage of Kotlin’s simplicity. Custom drawing and layout is now a simple function call, rather than having to do various carbon copies of view subclasses.

Take the grading table for example:

Written using the view class, this table contains:

  • There are three view classes, two of which require custom drawing rounded rectangles and stars
  • About 350 lines of Java code and 55 lines of XML

Written using Compose, this table contains:

  • All @composable functions are contained in the same file and language!
  • About 210 lines of Kotlin code

animation

Animation is one of Compose’s acclaimed features because of its simplicity and expressiveness. Our team is using Compose to build dynamic features that have greatly improved user satisfaction on the Play Store. With Compose’s declarative and animation API, it has never been easier to write continuous or parallel animations. Our team no longer worries about all the extremes of animation cancellation and callback chains. Lottie is a popular animation library that already provides an easy-to-use Compose API.

You can learn more about building animations with Compose by watching the video animation becomes a highly acclaimed feature of Compose.

Now you might be thinking: That all sounds great, but what about the library dependencies that provide views? It’s true that not all library developers implemented the Compose API, especially when we first migrated. However, Compose provides simple view interoperability through its ComposeView and AndroidView apis. In this way we have successfully integrated with popular libraries like ExoPlayer and YouTube Player.

performance

The Play Store works closely with the Jetpack Compose team to ensure that Compose can run as fast as the View framework and without lag. Because Compose needs to be packaged into the app (rather than as part of the Android framework), this is a daunting task. Rendering individual interface components on the screen is quick, but loading the entire Compose framework into your application’s memory takes a long time.

One of the biggest performance improvements for the Play Store with The adoption of Compose comes from the development of the baseline profile. While cloud profiles have been around for some time to help improve app startup times, they only work with API 28+ and don’t work well with apps with a frequent (weekly) update rhythm. To address this issue, the Play Store and the Android team worked together to develop Baseline Profiles: A developer predefined, packaged, application-specific profile that ships with your application, is fully compatible with cloud profiles, and can be defined at the application-specific and library levels (this feature is free for Compose developers!). . By rolling out the baseline profile, the Play Store saw a 40% reduction in initial page rendering time for its search results page. This is great progress!

Reusing interface components is the core mechanism that makes Compose great for rendering, especially in scrolling situations. Compose skips the recombination of composable items that are known to be skippable as much as possible (for example, they are immutable), but developers can also force composable items to be skippable if all parameters satisfy the @stable annotation. The Compose compiler also provides a handy guide on how to prevent specific functions from being skipped. When creating a large number of reusable interface components in the Play Store that are frequently used in scrolling situations, we found that unnecessary recombination increased lost frame time, resulting in stalling. We created a Modifier to make it easy to find these reorganizations in our debug Settings. By applying these techniques to our interface components, we were able to reduce stackage by 10-15%.

△ Reorganization visualization Modifiers in practice Blue (no reorganization), Green (1 reorganization)

Another key to optimizing Compose for the Play Store app is to develop a detailed end-to-end migration strategy for the entire app. In our initial integration experiments, we ran into a double-stack problem: running Compose and view class rendering simultaneously in a single user session was very memory intensive, especially on low-end devices. This can happen when the code is running on the same page, or when two different pages (for example, the Play Store home page and the search results page) are on different stacks. To improve this startup delay, it is important that we have a specific plan for the order and timing of the page migration to Compose. We also found it helpful to warm up some common classes before migrating to Compose for full rendering.

Separating Compose from the Android framework reduces the overhead for our team to directly contribute to Jetpack Compose, thereby shortening the turnaround time for improvements to the benefit of all developers. We worked with the Jetpack Compose team to roll out features like LazyList item type caching and quickly make lightweight fixes like additional object assignments.

Looking to the future

The adoption of Compose in the Play store has improved the happiness of our team of developers, as well as code quality and health. All of the new Play Store features are built on top of this framework, and Compose helps make apps faster and smoother to access. Due to the nature of our Compose migration strategy, we can’t accurately measure APK size changes or build rates, etc., but all the signs we are seeing are very positive!

Compose is the future of Android interface development and is helping to further optimize the Play Store. You are welcome to follow us for the latest content.