Understanding the performance benefits of ConstraintLayout

Posted by Takeshi Hagikura, Developer Programs Engineer


Since announcing href="https://developer.android.com/training/constraint-layout/index.html">ConstraintLayout
at Google I/O last year, we've continued to improve the layout's stability and
layout editor support. We've also added new features specific to
ConstraintLayout that help you build various type of layouts, such
as href="https://developer.android.com/training/constraint-layout/index.html#constrain-chain">introducing
chains and href="https://developer.android.com/training/constraint-layout/index.html#ratio">setting
size as a ratio. In addition to these features, there is a notable
performance benefit by using ConstraintLayout. In this post, we'll
walk through how you can benefit from these performance improvements.

How Android draws views?



To better understand the performance of ConstraintLayout, let's
take a step back and see how Android draws views.



When a user brings an Android view into focus, the Android framework directs the
view to draw itself. This drawing process comprises 3 phases:



  1. Measure


    The system completes a top-down traversal of the view tree to determine how
    large each ViewGroup and View element should be. When a
    ViewGroup is measured, it also measures its children.



  2. Layout


    Another top-down traversal occurs, with each ViewGroup determining
    the positions of its children using the sizes determined in the measure phase.



  3. Draw


    The system performs yet another top-down traversal. For each object in the view
    tree, a Canvas object is created to send a list of drawing commands
    to the GPU. These commands include the ViewGroup and
    View objects' sizes and positions, which the system determined
    during the previous 2 phases.










Figure 1. Example of how the measure phase traverses a view tree



Each phase within the drawing process requires a top-down traversal of the view
tree. Therefore, the more views you embed within each other (or nest) into the
view hierarchy, the more time and computation power it takes for the device to
draw the views. By keeping a flat hierarchy in your Android app layouts, you can
create a fast and responsive user interface for your app.




The expense of a traditional layout hierarchy



With that explanation in mind, let's create a traditional layout hierarchy that
uses LinearLayout and RelativeLayout objects.








Figure 2. Example layout



Let's say we want to build a layout like the image above. If you build it with
traditional layouts, the XML file contains an element hierarchy similar to the
following (for this example, we've omitted the attributes):




class="prettyprint"><RelativeLayout>
<ImageView />
<ImageView />
<RelativeLayout>
<TextView />
<LinearLayout>
<TextView />
<RelativeLayout>
<EditText />
</RelativeLayout>
</LinearLayout>
<LinearLayout>
<TextView />
<RelativeLayout>
<EditText />
</RelativeLayout>
</LinearLayout>
<TextView />
</RelativeLayout>
<LinearLayout >
<Button />
<Button />
</LinearLayout>
</RelativeLayout>


Although there's usually room for improvement in this type of view hierarchy,
you'll almost certainly still need to create a hierarchy with some nested views.



As discussed before, nested hierarchies can adversely affect performance. Let's
take a look at how the nested views actually affect the UI performance using
Android Studio's href="https://developer.android.com/studio/profile/systrace-commandline.html">Systrace
tool. We called the measure and layout phases for each ViewGroup
(ConstraintLayout and RelativeLayout) programmatically
and triggered Systrace while the measure and layout calls are executing. The
following command generates an overview file that contains key events, such as
expensive measure/layout passes, that occur during a 20-second interval:




class="prettyprint">python $ANDROID_HOME/platform-tools/systrace/systrace.py --time=20 -o ~/trace.html gfx view res



For more details about how you can use Systrace, see the href="https://developer.android.com/studio/profile/systrace.html">Analyzing UI
Performance with Systrace guide.



Systrace automatically highlights the (numerous) performance problems with this
layout, as well as suggestions for fixing them. By clicking the "Alerts" tab,
you will find that drawing this view hierarchy requires 80 expensive passes
through the measure and layout phases!



Triggering that many expensive measure and layout phases is far from ideal; such
a large amount of drawing activity could result in skipped frames that users
notice. We can conclude that the layout has poor performance due to the nested
hierarchy as well as the characteristic of RelativeLayout, which
measures each of its children twice.








Figure 3. Looking at the alerts from Systrace for the
layout variant that uses RelativeLayout



You can check the entire code on how we performed these measurements in our href="https://github.com/googlesamples/android-constraint-layout-performance">GitHub
repository.


The benefits of a ConstraintLayout object



If you create the same layout using ConstraintLayout, the XML file
contains an element hierarchy similar to the following (attributes again
omitted):




class="prettyprint"><android.support.constraint.ConstraintLayout>
<ImageView />
<ImageView />
<TextView />
<EditText />
<TextView />
<TextView />
<EditText />
<Button />
<Button />
<TextView />
</android.support.constraint.ConstraintLayout>


As this example shows, the layout now has a completely flat hierarchy. This is
because ConstraintLayout allows you to build complex layouts
without having to nest View and ViewGroup elements.



For example, let's look at the TextView and EditText
in the middle of the layout:








When using a RelativeLayout, you need to create a new
ViewGroup to align the EditText vertically with the
TextView:



class="prettyprint"><LinearLayout
android:id="@+id/camera_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_below="@id/title" >

<TextView
android:text="@string/camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:id="@+id/cameraLabel"
android:labelFor="@+id/cameraType"
android:layout_marginStart="16dp" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<EditText
android:id="@+id/cameraType"
android:ems="10"
android:inputType="textPersonName"
android:text="@string/camera_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
</RelativeLayout>
</LinearLayout>


By using ConstraintLayout instead, you can achieve the same effect
just by adding a constraint from the baseline of the TextView to
the baseline of the EditText without creating another
ViewGroup:








Figure 4. Constraint between EditText and TextView



class="prettyprint"><TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_creator="1"
app:layout_constraintBaseline_creator="1"
app:layout_constraintLeft_toLeftOf="@+id/activity_main_done"
app:layout_constraintBaseline_toBaselineOf="@+id/cameraType" />


When running the Systrace tool for the version of our layout that uses
ConstraintLayout, you see far fewer expensive measure/layout passes
during the same 20-second interval. This improvement in performance makes sense,
now that we're keeping the view hierarchy flat!








Figure 5. Looking at the alerts from Systrace for the
layout variant that uses ConstraintLayout



On a related note, we built the ConstraintLayout variant of our
layout using just the href="https://developer.android.com/training/constraint-layout/index.html">layout
editor instead of editing the XML by hand. To achieve the same visual effect
using RelativeLayout, we probably would have needed to edit the XML
by hand.


Measuring the performance difference



We analyzed how long every measure and layout pass took for two type of layouts,
ConstraintLayout and RelativeLayout, by using
href="https://developer.android.com/reference/android/view/Window.OnFrameMetricsAvailableListener.html">OnFrameMetricsAvailableListener,
which was introduced in Android 7.0 (API level 24). This class allows you to
collect frame-by-frame timing information about your app's UI
rendering.


By calling the following code, you can start recording per-frame UI actions:




class="prettyprint">window.addOnFrameMetricsAvailableListener(
frameMetricsAvailableListener, frameMetricsHandler);


After timing information becomes available, the app triggers the
frameMetricsAvailableListener() callback. We are interested in the
measure/layout performance, so we call href="https://developer.android.com/reference/android/view/FrameMetrics.html#LAYOUT_MEASURE_DURATION">FrameMetrics.LAYOUT_MEASURE_DURATION
when retrieving the actual frame duration.



class="prettyprint">Window.OnFrameMetricsAvailableListener {
_, frameMetrics, _ ->
val frameMetricsCopy = FrameMetrics(frameMetrics);
// Layout measure duration in nanoseconds
val layoutMeasureDurationNs =
frameMetricsCopy.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION);


To learn more about the other types of duration information that
FrameMetrics can receive, see the href="https://developer.android.com/reference/android/view/FrameMetrics.html">FrameMetrics
API reference.

Measurement results: ConstraintLayout is faster



Our performance comparison shows that ConstraintLayout performs
about 40% better in the measure/layout phase than RelativeLayout:








Figure 6. Measure / Layout (unit: ms, average of 100
frames)



As these results show, ConstraintLayout is likely to be more
performant than traditional layouts. Moreover, ConstraintLayout has
other features that help you build complex and performant layouts, as discussed
in the benefits of a ConstraintLayout
object
section. For details, see the href="https://developer.android.com/training/constraint-layout/index.html">Build
a Responsive UI with ConstraintLayout guide. We recommend that you use
ConstraintLayout when designing your app's layouts. In almost all
cases when you would have previously need a deeply-nested layout,
ConstraintLayout should be your go-to layout for optimal
performance and ease of use.


Appendix: Measurement environment



All the measurements above were performed in the following environment.
















Device
Nexus 5X
Android Version
8.0
ConstraintLayout version
1.0.2


What's next



Check out the href="https://developer.android.com/training/constraint-layout/index.html">developer
guide, the href="https://developer.android.com/reference/android/support/constraint/ConstraintLayout.html">API
reference documentation, and the href="https://medium.com/google-developers/building-interfaces-with-constraintlayout-3958fa38a9f7">article
on Medium to fully understand what ConstraintLayout can provide
for you. And once again, thank you to all who submitted feedback and issues over
the months since our alpha release of ConstraintLayout. We're truly
grateful that we were able to release the production-ready href="http://tools.android.com/recent/constraintlayout10isnowavailable">1.0
version of ConstraintLayout earlier this year.
As we continue to improve ConstraintLayout, please continue to send
us feedback using the Android issue tracker.



Komentar

Postingan populer dari blog ini

Google Play Referrer API: Track and measure your app installs easily and securely

Boosting developer success on Google Play

And the 2019 Google Play Award nominees are...