There are many benchmarks that compare the performance and rendering speed of XML and compose. For the most part, they all show that compose is faster. However, there are suggestions that these benchmarks do not give a complete picture of performance because they compare trivial solutions, such as single nesting or lack of a large number of views. It is also assumed that in critical cases, such as deep or wide view nesting, there will be different results. There are also opinions that it doesn't make sense to refactor code to compose in already existing large project, because the hybrid variant (compose+xml) will exist in the project for a long time, and rendering performance will suffer even more because of it. That's why I decided to compare more complex variants:
-deep and wide view nesting;
-influence of hybrid (compose+xml) state with respect to these cases.
-This comparison in no way claims to be absolutely correct for all types of devices, amounts of RAM or different types of displays.
-Measurements were made on Xiaomi mi 5s phone, MIUI Global 10.2, 12.15GHz quad-core processor, 3GB RAM, Android 8.0. Especially chose not the most powerful and top-end phone, so that the difference was more noticeable. Most people use similar devices.
-To reduce the amount of code, I did not calculate the density of the screen, because I know that I have 3.
-Use the 16 colors suggested by Android Studio to separate the views from each other.
-The measurement for each case is taken 10 times and the average is calculated. -If you have any questions about quality or quantity, the complete test code is given after each example and you can repeat it at any time.
I wanted to nest about 100 views into each other to make comparisons, but I ran into an xml parsing error: AAPT: error: failed to parse proto XML This error occurred when nesting more than 49 views. Therefore, in the comparisons below I have operated with exactly this number of nested elements. To visually display and control what is happening, I have assigned a different background color and some indents to each element. As you can see in the picture to the left, the red view has a raspberry view nested, the crimson view has a purple view nested, etc. This group of examples will show how deep nesting affects performance in different execution options: xml, code, compose, hybrid (xml+compose). These examples will be labeled as depth.
Along the way, I created another critical case - "very wide nesting". That is, I put also 49 elements in one view and colored them with different colors (see picture on the right). I purposely didn't use RecyclerView and LazyColumn for my comparisons. This group of examples will be marked as width. Each view is also colored to control what happens.
We now have the task of figuring out the rendering speed at each execution and answering a few questions:
a) will there be an advantage in using compose when nesting is deep and wide?
b) does the hybrid state affect rendering speed and performance in these cases?
We will use the segment between the two points for measurements. The start point after super.onCreate(savedInstanceState). Intermediate ― after super.onResume(). The final one is onWindowFocusChanged() which coincides with the full display of activity and the change of focus to the active state. The end point is after super.onResume(). The time will be measured by System.currentTimeMillis(), because we don't need to calculate absolute values, so there is no point in using more precise tools.
Let's start with the first example. Let's put 49 times FrameLayout into each other and color the background in the standard colors. Let's add indents.
<!--49 times-->
<FrameLayout>
<FrameLayout>
<FrameLayout>
...
</FrameLayout>
</FrameLayout>
</FrameLayout>
Full code
Table with the results:
Let's put the following example into LinearLayout 49 FrameLayout. I purposely didn't use RecyclerView to determine how the view would behave with wide nesting without optimizations.
<LinearLayout>
<!--49 times-->
<FrameLayout/>
<FrameLayout/>
<FrameLayout/>
...
</LinearLayout>
Full code
Table with the results:
In this example, we'll put 49 FrameLayout in, but in runtime. We'll also color it with standard colors and add indents. In this example, we know in advance that the xml will be faster because there is no xml parsing step. This example is very close to compose. Rather, the goal here is to find out how much influence legacy has accumulated over the years in View. As you will see below, the influence of legacy code is quite big.
//49 times
FrameLayout(this)
FrameLayout(this)
FrameLayout(this)
...
Full code
Table with the results:
In this example we will place the 49 FrameLayout vertically in the LinearLayout. Let's add colors to each element.
LinearLayout(this)
//49 times
FrameLayout(this)
FrameLayout(this)
FrameLayout(this)
Full code
https://gist.github.com/808fd9ac925cf8f53260726f6e54ef09
Table with the results:
In this example, let's finally start using compose. Let's nest Box 49 times inside each other to achieve deep nesting like in the xml example. Based on these two results, we will be able to compare whether deep nesting affects the rendering speed of the view hierarchy.
//49 times
Box()
Box()
Box()
...
Full code
Table with the results:
In this example, as in the xml, we'll make a wide nesting by inserting 49 Boxes into one Column. Just like in the example above, we intentionally didn't use the LazyColumn. Based on this example and the example above in the xml, we can conclude whether wide nesting affects the speed of rendering.
Column()
//49 times
Box()
Box()
Box()
...
Full code
https://gist.github.com/604ecdea027321df09d11778ce7d6bef
Table with the results:
In this example, we put 24 FrameLayout, then we put ComposeView. In ComposeView we'll put 24 Boxes (24 FrameLayout + 1 ComposeView + 24 Box = 49). This way we can assess whether the hybrid state during refactoring affects the performance and the rendering speed of the view. Let's assume that all FrameLayout is the remaining legacy, and Box is the refactored part.
<!--24 times-->
<FrameLayout>
<FrameLayout>
<FrameLayout>
...
<ComposeView>
//24 times
Box()
Box()
Box()
...
</ComposeView>
</FrameLayout>
</FrameLayout>
</FrameLayout>
Full code
Table with the results:
In this example, create 49 ComposeViews in a LinearLayout and put a Box in each one.
<!--49 times-->
<LinearLayout>
<ComposeView>
Box()
</ComposeView>
<ComposeView>
Box()
</ComposeView>
<ComposeView>
Box()
</ComposeView>
...
<LinearLayout>
Full code
Table with the results:
Conclusion
Let's summarize all the results in a graph.
The results were quite ambiguous. Compose turned out to be 50% faster than xml in the onPause() method, but 2 times slower in the onWindowFocusChanged() method.
Answering the two questions originally posed, we conclude:
Compose is a very powerful and useful tool, but as you can see, it won't solve all the problems for us. Using compose, like any other technology, requires compromises and accuracy in its application.