自从在17年GoogleI/O大会宣布了Constraintlayout,我们持续提升了布局的稳定性和布局编辑的支持。我们还为ConstraintLayout添加了一些新特性支持创建不同类型的布局,添加这些新特性,可以明显的提升性能,在这里,我门将讨论ContrainLayout是如何提升性能的。
Android是怎么绘制view的?
为了更好的理解constrainLayout 的性能,我们先回顾android是怎么绘制视图的。
当一个用户把一个视图呈现在眼前,android framework 直接让视图绘制自己,绘制的过程包括三个阶段:
measure
系统完成视图树的自顶向下遍历,以确定每个ViewGroup和View元素应该有多大。 当一个ViewGroup被测量时,它也测量它的子项。Layout
另一个自上而下遍历行为,每个ViewGroup使用在测量阶段确定的大小来确定其子级的位置。Draw
系统执行另一个自上而下的遍历。 对于视图树中的每个对象,都会创建一个Canvas对象,以向GPU显示绘图命令列表。 这些命令包括ViewGroup和View对象的大小和位置,系统在前两个阶段确定的。
绘图过程中的每个阶段都需要对视图树进行自顶向下的遍历。 因此,嵌入到视图层次结构中的视图越多,设备绘制视图所需的时间和计算能力就越多。 通过在您的Android应用布局中保持扁平的层次结构,您可以为您的应用创建快速响应的用户界面。
传统布局结构的开销
为了对着这个观点的说明,让我们创建一个使用LinearLayout和RelativeLayout对象的传统布局层次结构。
当我们想实现上面的界面,如果我们使用传统布局,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工具来看看嵌套视图如何实际影响UI性能。 我们以编程方式调用每个ViewGroup(ConstraintLayout和RelativeLayout)的度量和布局阶段,并在度量和布局调用执行时触发Systrace。 以下命令会生成一个概览文件,其中包含20秒间隔内发生的关键事件(例如,昂贵的度量/布局传递):
python $ANDROID_HOME/platform-tools/systrace/systrace.py --time=20 -o ~/trace.html gfx view res
Systrace会自动突出显示此布局的(许多)性能问题,以及修复这些问题的建议。 通过点击“警报”选项卡,您会发现绘制该视图层次结构需要80个昂贵的测量和布局阶段!
触发许多昂贵的措施和布局阶段是不是很理想; 如此大量的绘图活动可能导致用户注意到的跳帧。 我们可以得出这样的结论:由于嵌套层次结构会导致布局性能较差,像RelativeLayout每次会测量子节点两次。
ConstraintLayout 对象的好处
如果你使用ConstrainLayout 去创建相同的布局,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即可达到相同的效果:
<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秒间隔内,测量/布局合格率要低得多。 性能的改善是有意义的,现在我们保持视图层次平坦!
在相关说明中,我们仅使用布局编辑器构建了布局的ConstraintLayout变体,而不是手动编辑XML。 为了使用RelativeLayout实现相同的视觉效果,我们可能需要手动编辑XML。
Measuring 的性能不同
我们通过使用Android 7.0(API级别24)中引入的OnFrameMetricsAvailableListener,分析了每种度量和布局通过两种类型的布局ConstraintLayout和RelativeLayout所花费的时间。 此类允许您收集关于应用UI渲染的逐帧时间信息。
通过调用以下代码,您可以开始记录每帧UI操作:
window.addOnFrameMetricsAvailableListener(frameMetricsAvailableListener, frameMetricsHandler);
定时信息变为可用后,应用程序将触发frameMetricsAvailableListener()回调。 我们对度量/布局性能感兴趣,因此在检索实际帧持续时间时我们调用FrameMetrics.LAYOUT_MEASURE_DURATION。
Window.OnFrameMetricsAvailableListener {_, frameMetrics, _ ->val frameMetricsCopy = FrameMetrics(frameMetrics);// Layout measure duration in nanosecondsval layoutMeasureDurationNs = frameMetricsCopy.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION);
要了解有关FrameMetrics可以接收的其他类型的持续时间信息的更多信息,请参阅FrameMetrics API参考。
测试结果:ConstraintLayout is faster
我们的性能比较显示,ConstraintLayout在度量/布局阶段比RelativeLayout好40%左右:
正如这些结果所示,ConstraintLayout可能比传统布局更具性能。 此外,ConstraintLayout还有其他一些功能可以帮助您构建复杂和高性能的布局,正如在ConstraintLayout对象部分中所讨论的。 有关详细信息,请参阅使用ConstraintLayout指南构建响应式UI。 我们建议您在设计应用程序的布局时使用ConstraintLayout。 几乎在所有情况下,如果以前需要深度嵌套的布局,ConstraintLayout应该是您的最佳布局,以实现最佳性能和易用性。
附录:测量环境
以上所有测量均在以下环境中进行。
device | Nexus 5X |
---|---|
android version | 8.0 |
contraintLayout version | 1.0.2 |