绘制性能优化-ConstraintLayout

ConstraintLayout是2016年Google发布的,这种新的布局方式支持了编辑器的方式,从布局和性能上都做了一定的优化,同时也增加了一些新的概念,例如约束链和设置大小比例。本文从将会从性能上与传统的布局进行比较,并且给出使用在项目上的建议。

Android的绘制流程

首先回顾一下Android的绘制流程,方便我们更好的理解ConstraintLayout的性能。

Android绘制流程包括3个阶段:

1. 测量(Measure)

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

2. 布局(Layout)

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

3. 绘制(Draw)

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

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

层级消耗

绘制一个如下图所示的界面

界面

使用传统布局LinearLayout和RelativeLayout来完成上面的这个图片,需要下面的代码

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

层级最深需要4层,结合上面讲的Android绘制流程,Measure、Layout、Draw三个步骤都需要绘制子View,层级越深,绘制的所要花费的时间就越长,Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染。理论上来说两次回调的时间周期应该在16ms,如果超过了16ms我们则认为发生了卡顿。
但是我们使用ConstraintLayout,不论什么样场景都只需要一个层级就可以搞定。

扫描二维码关注公众号,回复: 2707921 查看本文章

性能比较

口说无凭,我们需要用真实的数据进行对比,网上有很多测试绘制性能的工具,例如Systrace和TraceView,Systrace和TraceView使用方法。这些工具都可以帮助我们分析UI卡顿的原因,使用这两个工具让我们做个对比:

  • Systrace

传统的方式绘制

ConstraintLayout绘制方式

Systrace会自动高亮这个布局的性能问题,以及一些修改的建议,通过点击“Alerts”标签,你会发现绘制这个View等级在测量和布局阶段需要80多次昂贵的传递,在测量和布局阶段触发很多昂贵的传递并不是我们想要的,大量的绘制活动会导致掉帧。而ConstraintLayout比传统的方式好了很多。

  • TraceView

传统的方式绘制

ConstraintLayout绘制方式

两幅图是在创建Activity的时候调用的方法,可以看出,传统的绘制方式调用方法的层级明显比ConstrainLayout的多。

通过这两个性能的比较,在比较复杂的情况下,ConstraintLayout性能表现还是很好的。

在ConstraintLayout 1.0的时候,在简单布局的情况下,ConstraintLayout的绘制的低于LinearLayout和RelativeLayout布局的绘制速度的。

ConstraintLayout 1.0 优势:
1. 一层布局解决所有的情况
2. 按照比例布局
3. 可视化编辑操作
4. 在复杂的情况下性能优于传统的布局

ConstrainLayout 1.0 劣势:
1. 在复杂的布局时,阅读比较费劲
2. 在简单的布局时,性能消耗比传统的布局要大
3. 因为引入新的概念,上手需要一些时间。
4. 存在一些bug和坑

ConstrainLayout这里有一篇很好的使用介绍

ConstraintLayout 1.1

最近Google升级了约束布局,修改了bug,更新了一些新的特性

百分比

在约束布局 1.0 版本中,需要使用两条引导线才能让视图根据百分比来占据屏幕。而在约束布局 1.1 版本中,通过允许您轻松地将任何视图限制为百分比宽度或高度,一切将变得很简单。

image

使用百分比指定按钮的宽度,以便在保持设计效果的同时适应可用空间。
所有视图都支持 layout_constraintWidth_percent 和 layout_constraintHeight_percent 属性。这些将导致约束被固定在可用空间指定百分比位置。 因此,使用几行 XML 代码就可以使 Button 或 TextView 展开并以百分比填充屏幕。

<Button
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintWidth_percent="0.7" />

链条

链条不是很特性,1.1修复1.0的一些bug。

通过 spread,spread_inside 和 packed,链条能够让您配置如何布置多个相关的视图。
app:layout_constraintVertical_chainStyle 属性可以作用于链条中的任何视图。 您可以设置它的值为 spread,spread_inside 或者 packed。

  • spread:均匀分配链中的所有视图
  • spread_inside:将第一个元素和最后一个元素放置在边缘上,并均匀分布其余元素
  • packed:将元素包裹在链条的中心

群组

在1.0的时候,如果你想隐藏一些view,只能调用每个view的setView方法,1.1新增群组的功能是非常好的。

一个群组并没有增加视图的层级——这实际上只是一种标记视图的方式。在下面的示例中,我们将标记 profile_name 和 profile_image 以供 id 配置文件引用。

当您有多个需要显示或陈列在一起的元素时,这将很有用。

<android.support.constraint.Group
    android:id="@+id/profile"
    app:constraint_referenced_ids="profile_name,profile_image" />

当定义名为 profile 的群组后,您可以为该群组设置可见性,并将其应用于 profile_name 和 profile_image。

prifile.setVisibility(GONE);
prifile.setVisibility(VISIABLE);

更多的新特性,可以看这篇文章

项目中检测绘制性能

  1. 利用UI线程Looper打印的日志
  2. 利用Choreographer
  3. OnFrameMetricsAvailableListener Android 7.0以后出来的新功能,用来检测每一帧中各个方法绘制所需要的时间

前两种方式具体使用可以看这篇文章

FrameMetrics

从 7.0(API 24)开始,安卓 SDK 新增 OnFrameMetricsAvailableListener 接口用于提供帧绘制各阶段的耗时,数据源与 GPU Profile 相同。
回调接口为 Window.FrameMetrics:

public interface OnFrameMetricsAvailableListener {
    void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation);
}

FrameMetrics存储了如下的数据

阶段 含义(纳秒) 备注
ANIMATION_DURATION 动画耗时
DRAW_DURATION 创建和更新DisplayList耗时
FIRST_DRAW_FRAME 布尔值,标志该帧是否为此 Window 绘制的第一帧
INPUT_HANDLING_DURATION 处理用户输入操作的耗时
INTENDED_VSYNC_TIMESTAMP 预期VSync到来的时间戳
LAYOUT_MEASURE_DURATION layout/measure耗时
SWAP_BUFFERS_DURATION GPU在等待GPU完成渲染的耗时
SYNC_DURATION 上传bitmap到GPU耗时
TOTAL_DURATION 整帧渲染耗时
UNKNOWN_DELAY_DRRATION 位置延迟
VSYNC_TIMESTAMP VSync时机到来的时间戳

使用ActivityFrameMetrics的效果:

看一下抖音项目中首页的帧绘制效率

可以看出来,第一帧总共花费时间是1.2s,这已经是很长的时间了,在网络请求后,又进行了一次绘制,花费了大概0.5s,如果是优化可以从这方面进行。MainActivity层级大搞是7-8层的样子,如果是用ConstraintLayout的话,至少可以减少一半的层级,可以在一定程度上减少绘制所花费的时间。

猜你喜欢

转载自blog.csdn.net/a296777513/article/details/80529916