管理 View
对象的层级结构的方式会显著影响应用性能。本页介绍如何评估视图层级结构是否拖慢了应用速度,并提供了一些解决可能出现的问题的策略。
本页面侧重于改进基于 View
的布局。有关改进 Jetpack Compose 性能的信息,请参阅Jetpack Compose 性能。
布局和测量性能
渲染流水线包含一个“布局和测量”阶段,在此阶段,系统会适当地放置视图层级结构中的相关项。“测量”部分确定 View
对象的大小和边界。“布局”部分确定 View
对象在屏幕上的位置。
这两个流水线阶段都会为它们处理的每个视图或布局产生少量开销。大多数情况下,此开销很小,不会显著影响性能。但是,当应用添加或移除 View
对象时,例如当 RecyclerView
对象回收或重用它们时,开销可能会更大。如果 View
对象需要考虑调整大小以满足其约束,则开销也可能更高。例如,如果您的应用在包裹文本的 View
对象上调用 SetText()
,则该 View
可能需要调整大小。
如果这些情况耗时过长,它们可能会阻止帧在允许的 16 毫秒内渲染,这可能导致帧丢失并使动画卡顿。
由于您无法将这些操作移动到工作线程(您的应用必须在主线程上处理它们),因此最好优化它们,使其尽可能少地占用时间。
管理复杂布局
Android 布局允许您在视图层级结构中嵌套 UI 对象。这种嵌套也可能导致布局开销。当您的应用处理一个对象进行布局时,应用也会对其布局的所有子项执行相同的处理。
对于复杂布局,有时只有在系统首次计算布局时才会产生开销。例如,当您的应用在 RecyclerView
对象中回收复杂的列表项时,系统需要布局所有对象。在另一个示例中,微不足道的更改可能会沿着链向上传播到父级,直到它们到达一个不影响父级大小的对象。
布局耗时过长的一个常见原因是 View
对象的层级结构相互嵌套。每个嵌套的布局对象都会增加布局阶段的开销。您的层级结构越扁平,布局阶段完成所需的时间就越少。
我们建议使用 布局编辑器创建 ConstraintLayout
,而不是 RelativeLayout
或 LinearLayout
,因为它通常更高效,并能减少布局的嵌套。然而,对于可以使用 FrameLayout
实现的简单布局,我们建议使用 FrameLayout
。
如果您正在使用 RelativeLayout
类,您可以通过使用嵌套的无权重 LinearLayout
视图来实现相同的效果,且成本更低。但是,如果您使用嵌套的带权重 LinearLayout
视图,布局成本会高得多,因为它需要多次布局传递,如下一节所述。
我们还建议使用 RecyclerView
而不是 ListView
,因为它可以回收单个列表项的布局,这既高效又可以提高滚动性能。
双重税收
通常,框架会单次执行布局或测量阶段。但是,在某些复杂的布局情况下,框架可能需要对层级结构中需要多次传递才能最终定位元素的部分进行多次迭代。执行多次布局和测量迭代被称为“双重税收”。
例如,当您使用 RelativeLayout
容器(它允许您相对于其他 View
对象的位置来定位 View
对象)时,框架会执行以下序列:
- 执行一次布局和测量传递,在此期间,框架会根据每个子对象的请求计算其位置和大小。
- 使用此数据,并考虑对象权重,以确定相关视图的正确位置。
- 执行第二次布局传递以最终确定对象的位置。
- 进入渲染过程的下一个阶段。
您的视图层级结构级别越多,性能损失的潜在可能性就越大。
如前所述,ConstraintLayout
通常比除 FrameLayout
之外的其他布局更高效。它不易出现多次布局传递,并且在许多情况下消除了嵌套布局的需要。
除了 RelativeLayout
之外的其他容器也可能增加双重税收。例如:
- 如果将
LinearLayout
视图设置为水平方向,可能会导致两次布局和测量传递。如果添加measureWithLargestChild
,垂直方向也可能发生两次布局和测量传递,在这种情况下,框架可能需要进行第二次传递以解析对象的正确大小。 GridLayout
也允许相对定位,但它通常通过预处理子视图之间的位置关系来避免双重税收。但是,如果布局使用权重或与Gravity
类一起填充,则预处理的优势就会丧失,如果容器是RelativeLayout
,框架可能需要执行多次传递。
多次布局和测量传递不一定是性能负担。但是,如果它们出现在不正确的位置,它们可能会成为负担。请注意以下条件之一适用于您的容器的情况:
- 它是视图层级结构中的根元素。
- 它下方有深层视图层级结构。
- 屏幕上填充了许多它的实例,类似于
ListView
对象中的子项。
诊断视图层级结构问题
布局性能是一个复杂且多方面的问题。以下工具可以帮助您识别性能瓶颈所在。某些工具提供的信息不那么明确,但可以提供有用的提示。
Perfetto
Perfetto 是一个提供性能数据的工具。您可以在 Perfetto UI 中打开 Android 跟踪。
配置文件 GPU 渲染
在 Android 6.0 (API level 23) 及更高版本设备上可用的设备端 Profile GPU Rendering 工具可以为您提供有关性能瓶颈的具体信息。该工具可让您查看每个渲染帧的布局和测量阶段需要多长时间。此数据可以帮助您诊断运行时性能问题,并帮助您确定需要解决哪些布局和测量问题。
在其捕获的数据的图形表示中,Profile GPU Rendering 使用蓝色表示布局时间。有关如何使用此工具的更多信息,请参阅剖析 GPU 渲染速度。
Lint
Android Studio 的 Lint 工具可以帮助您了解视图层级结构中的低效率。要使用此工具,请选择分析 > 检查代码,如图 1 所示。

有关各种布局项的信息显示在Android > Lint > 性能下。要查看更多详细信息,请点击每个项以展开它,并在屏幕右侧的窗格中显示更多信息。图 2 显示了一个展开信息的示例。

点击某个项会在右侧窗格中显示与该项相关的问题。
要了解此领域中的特定主题和问题,请参阅 Lint 文档。
布局检查器
Android Studio 的布局检查器工具提供应用程序视图层级结构的可视化表示。它是导航应用程序层级结构的好方法,提供特定视图父链的清晰可视化表示,并允许您检查应用程序构建的布局。
布局检查器呈现的视图还可以帮助识别由双重税收引起的性能问题。它还可以提供一种方法来识别嵌套布局的深层链,或者包含大量嵌套子项的布局区域,这些都可能是性能开销的来源。在这些情况下,布局和测量阶段可能代价高昂,并导致性能问题。
如需了解更多信息,请参阅使用布局检查器和布局验证调试您的布局。
解决视图层级结构问题
解决视图层级结构引起的性能问题的基本概念在实践中可能很困难。防止视图层级结构导致性能损失包括扁平化视图层级结构和减少双重税收。本节讨论实现这些目标的策略。
移除冗余嵌套布局
ConstraintLayout
是一个 Jetpack 库,具有大量不同的机制用于在布局中定位视图。这减少了嵌套一个 ConstaintLayout
的需求,并有助于扁平化视图层级结构。与使用其他布局类型相比,使用 ConstraintLayout
扁平化层级结构通常更简单。
开发者经常使用比必要更多的嵌套布局。例如,一个 RelativeLayout
容器可能包含一个也是 RelativeLayout
容器的子项。这种嵌套是冗余的,并增加了视图层级结构不必要的开销。Lint 可以为您标记此问题,从而减少调试时间。
采用 merge 或 include
冗余嵌套布局的一个常见原因是 <include>
标签。例如,您可以如下定义一个可重用布局:
<LinearLayout> <!-- some stuff here --> </LinearLayout>
然后,您可以添加一个 <include>
标签,将以下项添加到父容器中:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/app_bg" android:gravity="center_horizontal"> <include layout="@layout/titlebar"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" android:padding="10dp" /> ... </LinearLayout>
上述 include 不必要地将第一个布局嵌套在第二个布局中。
<merge>
标签有助于防止此问题。有关此标签的信息,请参阅使用 <merge> 标签。
采用更廉价的布局
您可能无法调整现有的布局方案,使其不包含冗余布局。在某些情况下,唯一的解决方案可能是通过切换到完全不同的布局类型来扁平化您的层级结构。
例如,您可能会发现 TableLayout
提供了与更复杂的、具有许多位置依赖关系的布局相同的功能。Jetpack 库 ConstraintLayout
提供了与 RelativeLayout
类似的功能,以及更多有助于创建更扁平、更高效布局的功能。