Jetpack Compose accelerates UI development and improves Android development. However, consider the following sections before adding a new tool to a project, as it can affect the build, APK, and runtime performance.
APK size and build times
This section compares how the following two apps are impacted when adding or migrating to Jetpack Compose:
- Tivi: fully migrated to Compose, removing AppCompat and Material Design Components libraries.
compose_recyclerviewbranch) adds Compose only using it for items of a
RecyclerView. All other dependencies are the same.
Adding libraries to your project affects the APK size. See how Compose affects the size of the projects mentioned earlier. The following results are for the minified release APK of each project with resource and code shrinking enabled, using R8 full mode, and measured using APK Analyzer.
Tivi has a 46% reduction in APK size, from 4.49 MB to 2.39 MB. It also has a 17% reduction in method count after fully migrating to Compose. The XML lines of code decrease by 76%.
Sunflower has a 19% increase in APK size, from 2,407 KB to 2,982 KB, when Compose is added to the project. Since Compose is barely used in the Sunflower branch, the project doesn't benefit from Compose-only APK size benefits such as removing the AppCompat dependency from the project.
Using the same projects, measure how Compose affects build performance.
The mean build time for Tivi before migrating to Compose is 108.71 seconds. After fully migrating to Compose, the mean build time decreased to 76.96 seconds—a 29% decrease in build time. This time reduction is largely influenced by Hilt being faster in Android Gradle Plugin 7.0, and being able to remove data binding and Epoxy from the project—which uses kapt annotation processors.
Sunflower, however, experiences a 7.6% increase of its median build time, from 11.57 seconds to 12.45 seconds.
When you adopt Compose in your app, you can see an increase in APK size and build time that decreases and surpasses the original metric once the project is fully migrated to Compose.
This section covers topics related to runtime performance in Jetpack Compose to help understand how Jetpack Compose compares to the View system's performance, and how you can measure it.
When portions of the UI are invalid, Compose tries to recompose just the portions that need to be updated. Read more about this in the Lifecycle of composables and Jetpack Compose phases documentation.
Baseline Profiles are an excellent way of speeding up common user journeys. Including a Baseline Profile in your app can improve code execution speed by about 30% from the first launch by avoiding interpretation and just-in-time (JIT) compilation steps for included code paths.
The Jetpack Compose library includes its own Baseline Profile and you automatically get these optimizations when you use Compose in your app. However, these optimizations only affect code paths within the Compose library, so we recommend that you add a Baseline Profile to your app to cover code paths outside of Compose.
Comparison with the View system
Due to the design and lessons learned from the View system, Jetpack Compose outperforms it.
Everything extends View
View that draws on the screen, such as
ImageView, requires memory allocations, explicit state tracking, and various
callbacks to support all use cases. Furthermore, the custom
View owner needs
to implement explicit logic to prevent redrawing when it isn't
necessary—for example, for repetitive data processing.
Jetpack Compose addresses this in a few ways. Compose doesn't have explicit
updatable objects for drawing views. UI elements are simple composable functions
whose information is written to the composition in a replayable way. This helps
cut down explicit state tracking, memory allocations, and callbacks to only the
composables that require said features instead of requiring them by all
extensions of a given
Furthermore, Compose provides smart recompositions, replaying the previously drawn result if you don't need to make changes.
Multiple layout passes
Traditional ViewGroups have a lot of expressiveness in their measure and layout APIs that make them prone to multiple layout passes. These multiple layout passes can cause exponential work if done at specific nested points in the view hierarchy.
Jetpack Compose enforces a single layout pass for all layout composables via its API contract. This lets Compose efficiently handle deep UI trees. If multiple measurements are needed, Compose has intrinsic measurements.
View startup performance
The View system needs to inflate XML layouts when showing a particular layout for the first time. This cost is saved in Jetpack Compose since layouts are written in Kotlin and compiled like the rest of your app.
In Jetpack Compose 1.0, there are notable differences between the performance of
an app in
release modes. For representative timings, always
release build instead of
debug when profiling your app.
To check how your Jetpack Compose code is performing, you can use the Jetpack Macrobenchmark library. To learn how to use it with Jetpack Compose, see the MacrobenchmarkSample project.
The Jetpack Compose team also uses Macrobenchmark to catch any regressions that can happen. For example, see the benchmark for lazy column and its dashboard to track regressions.
Compose profile installation
Since Jetpack Compose is an unbundled library, it doesn't benefit from Zygote that preloads the View system's UI Toolkit classes and drawables. Jetpack Compose 1.0 utilizes profile installation for release builds. Profile installers let apps specify critical code to be ahead-of-time (AOT) compiled at installation time. Compose ships profile installation rules which reduce startup time and jank in Compose apps.