avatarElye - A One Eye Developer

Summary

The article compares the rendering performance of Jetpack Compose, Custom View, and SurfaceView in Android development, concluding that SurfaceView outperforms the other two, especially in complex rendering tasks.

Abstract

The author of the article expresses a strong belief in Jetpack Compose as the future of Android UI development, noting its versatility for canvas drawing and game development. However, through an experiment using a recursive tree drawing design, the author finds that SurfaceView significantly outperforms Jetpack Compose and Custom View in rendering complex graphics. The experiment involves modifying a tree-drawing algorithm to increase the number of branches, which exponentially increases the rendering complexity. The results show that SurfaceView, when wrapped in a Scroll View and using Coroutines, is much faster and smoother than Jetpack Compose and Custom View, which exhibit significant lag and jankiness at higher tree depths. The author also provides insights into optimizing the rendering process and acknowledges that while Jetpack Compose is a powerful tool, it may not be suitable for all rendering tasks, and developers should be proficient in using SurfaceView for complex rendering scenarios.

Opinions

  • The author strongly endorses Jetpack Compose for Android UI development, considering it the future of Android UIs.
  • Despite the belief in Jetpack Compose, the author acknowledges that SurfaceView remains superior for complex rendering tasks, as evidenced by performance tests.
  • The author suggests that developers should not abandon traditional views like SurfaceView, as they are still necessary for certain use cases, particularly those involving complex rendering.
  • Romain Guy's opinion is cited, emphasizing that SurfaceView is essential for interoperability between different renderers and that it remains useful alongside Jetpack Compose.
  • The author notes that the comparison between SurfaceView and Jetpack Compose is not entirely fair due to their different underlying engines and intended use cases, with SurfaceView being designed for low-level drawing and hardware rendering.
  • The author recommends that developers should be aware of the limitations of Jetpack Compose and know when to use SurfaceView for optimal performance in Android applications.

Learning Android Development

SurfaceView still Outperform Jetpack Compose in Rendering

Using a recursively drawing design that will stress out Jetpack Compose processing.

Photo by Gonard Fluit on Unsplash

Jetpack Compose is the future! I strongly believe that. It is not just having normal UI components. It can also be used for Canvas drawing, as simple game development.

With that, I’m curious, does that mean with Jetpack Compose, we won’t need Surface View anymore as well?

To experiment with that, I use a design, that recursively draws to render, which recursively draws the branches to form a tree shape. It’s a design from Vivek Sharma

Design from https://twitter.com/V9vek/status/1455971300501966852

Modified Design

The original design just appends 2 branches to its root recursively and keeps drawing, till the designated tree depth is provided.

// Right branch
branch(angle = branchAngle + branchAngleDifference, canvas)
// Left branch
branch(angle = branchAngle - branchAngleDifference, canvas)

I just modified to having 3 branches for each root

// Right branch
branch(angle = branchAngle + branchAngleDifference, canvas)
// Center branch
branch(angle = branchAngle, canvas)
// Left branch
branch(angle = branchAngle - branchAngleDifference, canvas)

As a result, the drawn tree of 3 separate branches becomes a magnitude denser than the 2 separate branches tree as the tree depth increases.

In referring to the diagrams above, we can clearly see the complexity grows exponentially as the tree depth increases. i.e.

  • at depth 15, 3-separate branches hit almost 4.8 million branches
  • at depth 15, 2-separate branches hit only 1.6 thousand branches. (i.e. that make the drawing for 2-separate branches relatively quick event in tree depth of 15)

But for 3-separate branches, with the deeper tree depth, it will generate many branches that will be useful to test out the performance of the drawing.

Besides, to ensure I get the optimum compilation, I turn on all the known optimization below

// Turn off debugging feature
debuggable false
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true

// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
shrinkResources true

// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile(
        'proguard-android-optimize.txt'),
        'proguard-rules.pro'

Let me know if I miss any other optimization setting. Thanks.

The design is here.

The variants

For this complex tree, I draw 3 variants

  1. Jetpack Compose — wrap in an AbstractComposeView and a Scroll View
  2. Custom View — A normal Android custom view wrap in a Scroll View
  3. Surface View — A Surface View wrap in a Scroll View and using Coroutine

Note: - Only a SurfaceView I can use Coroutine, as in CustomView, if we use another thread, the drawing will be off coordinate, due to share of Canvas. More is explained in the StackOverflow. - When I try wrap Coroutine over the composable drawing, it is stated “calls to launch should happen inside a launched effect and not composition”. Further elaboration in the StackOverflow.

The result

When running on tree-depth 2 to 9, the result is relatively fast, and hard to compare against the 3 approaches used. Beginning tree-depth of 10, we can start to see significant differences.

The result obtained as below

Both Custom View and Jetpack Compose result is relatively similar. But the Surface View is significantly faster than the other two.

The slowest I can measure where both Custom View and Jetpack Compose can be more than 800x slower than Surface!

With tree depth 11, we can also feel the slowness with our eye as shown below. The surface is almost immediate,y, while the other two take about 2 seconds.

Not only the rendering speed differs, when scrolling on it, both Custom and Compose view janks as well.

Notice the GIF below, the surface view moves smoothly per the pointer, but the Custom and Jetpac Compose view, lag behind the pointer movements.

The above is for Tree-Depth 12. When we use deeper tree-depth, the janky increases further.

Clearly, Surface View is still superior in terms of rendering performance. This is also confirmed by Romain Guy in Reddit

SurfaceView is mostly meant to allow interop between multiple renderers: the UI toolkit and a video player, or an OpenGL/Vulkan rendering engine, etc. So yes SurfaceView is still very much useful in that regard with Compose.

Here are a couple of apps I wrote that use both Compose + SurfaceView to render content with a 3D rendering engine:

https://github.com/romainguy/sample-materials-shop/

https://github.com/romainguy/sample-wake-me-up

Looks like, other than Jetpack Compose, we still need to learn some level of View, at least for SurfaceView.

**UPDATE**

To be fair, Surface View is not a normal view we use, and the underlying engine is also different. Further explanation from Romain Guy in Reddit.

These are not comparable. SurfaceView is meant for low-level drawing. The UI toolkits are not.

You are comparing software rendering vs hardware rendering. Normally GPU rendering should win but the fact that you emit individual drawLine() calls instead of batches with drawLines() is incredibly inefficient for the hardware accelerated pipeline. I suspect most of the performance difference come from that.

In short, there will be cases, it’s more suitable for Surface View while there will be cases, we’ll just use Jetpack Compose. Hence I agree with Romain that this is not an apple-to-apple comparison.

We just need to bear in mind, Jetpack Compose is not the only mean of UI rendering we have in Android. For complex rendering, Surface View is still preferred as it’s built meant for that.

Android App Development
Mobile App Development
AndroidDev
Jetpack Compose
Android Ui
Recommended from ReadMedium