(译)理解ConstraintLayout性能上的好处

本文介绍了ConstraintLayout对于性能方面的好处并和传统布局做了比较。

《钱塘湖春行》
孤山寺北贾亭西, 水面初平云脚低。
几处早莺争暖树, 谁家新燕啄春泥。
乱花渐欲迷人眼, 浅草才能没马蹄。
最爱湖东行不足, 绿杨阴里白沙堤。
-唐,白居易

本文首发:http://yuweiguocn.github.io/

关于ConstraintLayout的使用介绍请查看ConstraintLayout的使用

原文链接:Understanding the performance benefits of ConstraintLayout

自从去年Google I/O大会发布了ConstraintLayout,我们一直在持续提升布局的稳定性及布局编辑器的支持。我们也添加了针对ConstraintLayout的新特性来帮助你构建不同类型的布局。例如约束链和设置大小比例。除了这些特性,使用ConstraintLayout有一个值得注意的性能上的好处。在这篇文章中,我们会看看ConstraintLayout带来哪些性能提升。

Android是怎样绘制View的?

为了更好的理解ConstraintLayout的性能,我们来回顾一下Android是怎样绘制View的。

当用户让一个Android view获得焦点,Android框架会指导View绘制它自己。这个处理过程包括3个阶段:

1.测量(Measure)

系统完成View树从上到下的遍历决定每个ViewGroup有多大和View元素是什么。当一个ViewGroup被测量,它也会测量它的子View。

2.布局(Layout)

另一个从上到下遍历的发生,每个ViewGroup使用在测量阶段决定的大小确定子View的位置。

3.绘制(Draw)

没错,又一个从上到下的遍历。对于View树中的每个对象,创建的Canvas对象用于发送绘制命令列表到GPU。这些命令包括系统在前两个阶段确定的ViewGroup和View对象的大小和位置。

图1 测量阶段遍历一个View树的示例

绘制过程中的每个阶段需要从上到下遍历View树。因此,在View层级中互相嵌入的View越多,设备用于绘制View的所需时间和计算能力越多。通过在应用布局保持扁平层级,你可以创建一个快速响应用户界面的应用。

传统布局层级的消耗

了解了View的绘制过程,我们使用LinearLayout和RelativeLayout创建一个传统布局层级。

图2 布局示例

我们创建一个如上图所示的布局。如果使用传统布局,XML文件包含的元素和下面类似:

<RelativeLayout>
  <ImageView />
  <ImageView />
  <RelativeLayout>
    <TextView />
    <LinearLayout>
      <TextView />
      <RelativeLayout>
        <EditText />
      </RelativeLayout>
    </LinearLayout>
    <LinearLayout>
      <TextView />
      <RelativeLayout>
        <EditText />
      </RelativeLayout>
    </LinearLayout>
    <TextView />
  </RelativeLayout>
  <LinearLayout >
    <Button />
    <Button />
  </LinearLayout>
</RelativeLayout>

尽管在这种类型的View层级中有改进的余地,但你仍需要创建一些嵌套View层级。

正如之前说过的,嵌套层级会影响性能。我们使用Android Studio的Systrace工具看看嵌套层级对UI性能有哪些实际影响。我们用编程的方式调用每个ViewGroup(ConstraintLayout 和 RelativeLayout)的测量和布局并且当测量和布局执行的时候触发Systrace。下面的命令会生成一个包含关键事件的概览文件,例如昂贵的测量/布局的传递,时间在20秒以内。

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

关于可以怎样使用Systrace的详情,请查看Analyzing UI Performance with Systrace指南。

Systrace会自动高亮这个布局的性能问题,以及一些修改它们的建议。通过点击"Alerts"标签,你会发现绘制这个View层级在测量和布局阶段需要80多个昂贵的传递!

在测量和布局阶段触发很多昂贵的传递并不是我们想要的,大量的绘制活动会导致掉帧引起用户的注意。我们可以得出结论由于嵌套层级会降低性能,正如RelativeLayout会测量它的子View两次。

图3 从Systrace查看使用RelativeLayout布局的警告

可以从GitHub获取完整源码查看我们是怎样执行测量的。

ConstraintLayout的好处

如果你使用ConstraintLayout创建同样的布局,XML文件包含的元素层级就像下面这样:

<android.support.constraint.ConstraintLayout>
  <ImageView />
  <ImageView />
  <TextView />
  <EditText />
  <TextView />
  <TextView />
  <EditText />
  <Button />
  <Button />
  <TextView />
</android.support.constraint.ConstraintLayout>

如上所示,布局有一个完全扁平的层级。这是由于ConstraintLayout允许你创建复杂的布局不用嵌套View和ViewGroup元素。

例如,我们来看下布局中间的TextView和EditText:

当使用RelativeLayout,你需要创建一个ViewGroup垂直对齐EditText和TextView:

<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>

通过使用ConstraintLayout,你可以实现同样的效果,只需要从TextView的基线添加一个约束到EditText的基线,不用创建其它ViewGroup:

图4 EditText和TextView之间的约束

<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" />

当对我们使用ConstraintLayout的布局运行Systrace工具,你可以看到在同样20秒内只有很少的昂贵测量/布局传递。这对于提升性能是有意义的,现在我们保持了View层级的扁平化。

图5 从Systrace查看使用ConstraintLayout布局的警告

有一点需要注意,我们可以只使用布局编辑器构建ConstraintLayout布局而不用手动编辑XML文件。对于使用RelativeLayout实现同样的效果,我们可能需要手动编辑XML文件。

测量性能差异

我们分析了两种类型的布局ConstraintLayout和RelativeLayout的测量和布局传递有多长,通过使用 OnFrameMetricsAvailableListener,Android 7.0 (API level 24)引入。这个类可以收集应用的UI渲染帧与帧的时间信息。

通过调用下面的代码,可以开始记录每帧的UI动作:

window.addOnFrameMetricsAvailableListener(
        frameMetricsAvailableListener, frameMetricsHandler);

时间信息可用之后,应用会触发frameMetricsAvailableListener()回调。我们感兴趣的是测量/布局的性能,因此当我们接收到实际的帧时间调用FrameMetrics.LAYOUT_MEASURE_DURATION

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

关于FrameMetrics可以接收的其它类型的信息,请查看FrameMetrics API。

测量结果:ConstraintLayout更快

我们的性能比较显示ConstraintLayout在测量/布局阶段的执行要比RelativeLayout好40%:

图6 测量/布局(单位:毫秒,平均100帧)

上面的结果显示,ConstraintLayout可能比传统布局有更好的性能。此外,ConstraintLayout有其它特性可以帮助你构建复杂和高性能布局,正如ConstraintLayout的好处部分所说。详情请查看ConstraintLayout的使用。我们推荐你使用ConstraintLayout构建你的布局。当之前你需要多层嵌套的布局,ConstraintLayout可以让你的布局有最优的性能和易用性。

附录:测量环境

上面所有的测量在以下环境执行。

Device Android Version ConstraintLayout version
Nexus 5X 8.0 1.0.2

What's next

查看ConstraintLayout的使用API文档Medium上的文章完全理解ConstraintLayout可以为你提供什么。再次感谢我们发布ConstraintLayout alpha版本以来提交反馈和问题的所有人。我们真心感激年初发布的ConstraintLayout 1.0版本。我们会继续提升ConstraintLayout,请继续使用Android issue tracker给我们发送反馈。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容