了解使用 ConstraintLayout 的性能优势

原文:谷歌开发者中文博客
了解使用 ConstraintLayout 的性能优势

发布人:开发者计划工程师 Takeshi Hagikura

自从在去年的 Google I/O 大会上发布 ConstraintLayout 以来,我们一直不断改进该布局的稳定性,完善对布局编辑器的支持。我们还针对ConstraintLayout增加了一些新功能,帮助您构建不同类型的布局,例如引入链按比例设置大小。除了这些功能之外,使用ConstraintLayout还可以获得一项显著的性能优势。在本博文中,我们将向您介绍如何从这些性能改进中获益。

Android 如何绘制视图?

为了更好地理解ConstraintLayout的性能,我们先回过头来看看 Android 如何绘制视图。

当用户将某个 Android 视图作为焦点时,Android 框架会指示该视图进行自我绘制。这个绘制过程包括 3 个阶段:

1.测量
系统自顶向下遍历视图树,以确定每个ViewGroupView元素应当有多大。在测量ViewGroup的同时也会测量其子对象。
2.布局
系统执行另一个自顶向下的遍历操作,每个ViewGroup都根据测量阶段中所确定的大小来确定其子对象的位置。
3.绘制
系统再次执行一个自顶向下的遍历操作。对于视图树中的每个对象,系统会为其创建一个Canvas对象,以便向 GPU 发送一个绘制命令列表。这些命令包含系统在前面 2 个阶段中确定的ViewGroupView对象的大小和位置。

图 1. 测量阶段如何遍历视图树的示例

绘制过程中的每个阶段都需要对视图树执行一次自顶向下的遍历操作。因此,视图层次结构中嵌入(或嵌套)的视图越多,设备绘制视图所需的时间和计算功耗也就越多。通过在 Android 应用布局中保持扁平的层次结构,您可以为应用创建响应快速而灵敏的界面。

传统布局层次结构的开销

请牢记上述解释,下面我们来创建一个使用LinearLayoutRelativeLayout对象的传统布局层次结构。

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

尽管一般来说,这种类型的视图层次结构都有改进的空间,但您几乎必定还需要创建一个包含一些嵌套视图的层次结构。

如前所述,嵌套的层次结构会给性能造成负面影响。我们使用 Android Studio 的 Systrace 工具来看看嵌套视图对界面性能到底有何实际影响。我们通过编程方式针对每个ViewGroupConstraintLayoutRelativeLayout)调用了测量和布局阶段并在执行测量和布局调用期间触发了 Systrace。以下命令可生成一个包含 20 秒间隔周期内发生的关键 Event 的概览文件,例如开销巨大的测量/布局阶段:

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

有关如何使用 Systrace 的详细信息,请参阅使用 Systrace 分析界面性能指南。

Systrace 会自动突出显示此布局中的(大量)性能问题,并给出修复这些问题的建议。通过点击“Alerts”标签,您会发现,绘制此视图层次结构需要反复执行 80 次的测量和布局阶段,开销极为庞大!

触发开销如此庞大的测量和布局阶段当然很不理想,如此庞大的绘制 Activity 会导致用户能够觉察到丢帧的现象。我们可以得出这样的结论:这种嵌套式层次结构和RelativeLayout(会对其每个子对象重复测量两次)的特性导致性能低下。

图 3. 观察 Systrace 针对使用RelativeLayou 的布局版本发出的提醒

您可以在我们的GitHub 代码库中查看我们用来执行这些测量的完整代码。

ConstraintLayout 对象的优势

如果您使用ConstraintLayout来构建相同的布局,XML 文件会包含类似于下面这样的元素层次结构(再次忽略属性):

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

如本例所示,现在,该布局拥有一个完全扁平的层次结构。这是因为ConstraintLayout允许您构建复杂的布局,而不必嵌套ViewViewGroup元素。

举个例子,我们来看一下布局中间的TextViewEditText


使用RelativeLayout时,您需要创建一个新的ViewGroup来垂直对齐EditTextTextView

<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 秒间隔周期内执行的测量/布局次数大大减少,开销也随之大大减少。这种性能的改进很有意义,现在,我们保持了扁平的视图层次结构!

***图 5. ***观察 Systrace 针对使用ConstraintLayout的布局版本发出的提醒

同样值得一提的是,我们构建ConstraintLayout版本的布局时仅仅使用了布局编辑器,而不是手工编辑 XML。而要使用RelativeLayout来实现同样的视觉效果,我们很可能必须手工编辑 XML。

测量性能差异

我们使用 Android 7.0(API 级别 24)中引入的 OnFrameMetricsAvailableListener 分析了ConstraintLayoutRelativeLayout这两种类型的布局所执行的每次测量和布局操作所花费的时间。通过该类,您可以收集有关应用界面渲染的逐帧时间信息。

通过调用以下代码,您可以开始记录每个帧的界面操作:

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应当成为您优化性能和易用性的不二之选。

附录:测量环境

1 2
设备 Nexus 5X
Android 版本 8.0
ConstraintLayout 版本 1.0.2

上述所有测量均在以上环境中执行。

后续计划

查看[开发者指南](https://developer.android.google.cn/training/c
onstraint-layout/index.html)、API 参考文档媒体文章,以完全理解ConstraintLayout能够给您带来什么。再次感谢您,感谢自从我们的 Alpha 版ConstraintLayout发布以来的几个月里提交反馈和问题的所有同仁。我们得以在今年早些时候发布ConstraintLayout 1.0 正式版,离不开您的支持,我们在此谨致以诚挚的谢意!我们将继续改进ConstraintLayout,请您继续使用 Android Issue Tracker 向我们发送反馈。

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

推荐阅读更多精彩内容