Android - 布局性能:程序化 vs XML
Posted
技术标签:
【中文标题】Android - 布局性能:程序化 vs XML【英文标题】:Android - Layouts performance: Programmatic vs XML 【发布时间】:2016-06-04 17:31:44 【问题描述】:作为一名 android 开发人员,不知道是通过编程方式声明布局还是通过 XML 声明布局更好,这让我很困扰。
我已经阅读了关于 SO 的 this 和 this 问题,但他们都没有回答我的问题:
什么更高效:以编程方式编写布局或在 xml 文件中声明它们?
请注意,我只询问性能,我不想要基于其他因素的答案。
另外,我正在寻找一个非常技术性的答案。如果需要,请提供指向 AOSP 代码的链接以证明您的回答是合理的(您可以假设 Android 版本是 Marshmallow)。如果使用两种不同的方式比较 巨大 布局的加载时间,最好指出实验/论文/基准。
【问题讨论】:
你想要达到什么样的性能?无论如何,布局最终都会编译成代码,所以真的没有太大的区别。 比较 Activity 加载时间我会很满意,尽管还有其他一些基准需要衡量。 @Tdorno 这是否意味着 XML 有一个缺点,因为它必须被处理成代码? 【参考方案1】:对于大多数实际意图和目的,这两种方法都没有显着的性能影响。如果您需要对大量特定布局进行充气,这可能是相关的,此时您可以尝试自己对其进行基准测试,看看是否有任何真正的区别,但否则我很难想象一个结果无论哪种方式都会产生重大影响。
假设你有一个布局:
<LinearLayout xmlns:android="..."
android:layout_
android:layout_
android:orientation="vertical"
... >
<Button
android:id="@+id/some_button"
android:layout_
android:layout_
android:background="@drawable/my_button_bg"
android:text="Hello World" />
<!-- other views ... -->
</LinearLayout>
Android 会将此文件编译为二进制格式并将其打包到 APK 中。当您在运行时使用LayoutInflater
时,它会将这种二进制格式的块加载到内存中并对其进行解析,并根据内容构建视图层次结构,这与您在代码中手动执行的操作非常相似。该解析全部在本机代码中完成,因此它可能比您在 java 中的典型 XML 解析优化得多。
LayoutInflater
在遇到标签时使用反射构造视图(例如<Button .../>
)。第一次它必须查找该特定视图的构造函数;此后它将缓存构造函数以便以后更快地访问。
您通常会调用像button.setText(...)
、button.setBackground(...)
等的mutators,通常视图会在膨胀期间调用这些方法。也就是说,通过视图的构造函数遍历的代码路径将根据从二进制 XML 格式解析的属性执行这些更改。这是因为LayoutInflater
使用了接受AttributeSet
的两参数构造函数。这意味着当您手动构建视图时,其中一些方法可能最终会被调用两次。
例如,以上面示例布局中的按钮为例。按钮已经有一个默认背景(这实际上是如何提供的,这本身很有趣,但在这里不是很重要),所以即使只用Context
调用单参数构造函数,仍然会得到一个带有默认背景的Button
。换句话说,代码路径包括使用默认背景图像对setBackground(...)
(或一些等价物)的调用。然后,您必须使用 XML 文件中命名的自定义可绘制资源自己调用 setBackground(...)
。很难说这会产生什么影响,因为它实际上取决于个人视图的实施以及您正在做出的突变。
最后一个想法:我在专业环境中构建了一个应用程序,避免了所有 XML 的使用(尽可能包括布局以外的东西)。我可以毫不犹豫地告诉你,这非常烦人,并且大大增加了开发时间。我非常擅长它,它仍然比在 XML 中完成它需要更长的时间。另外:
代码往往非常冗长 您不会受益于 IDE 中围绕 XML 布局构建的所有工具 即使没有工具,仅 XML 文件就可以为您提供视图层次结构的清晰表示 您可能需要了解 UI 框架的某些特质(其中一些可能取决于 API 级别) 并非总是可以将 XML 属性映射到 java 中的相应 mutator 方法(有时又取决于 API 级别) 计算尺寸变得更加有趣 记住在哪些 LayoutParams 中使用的脑力体操很棒可能还有其他原因,但这些只是我想不到的。不要误会我的意思,很多时候手动操作视图和层次结构很有价值,但我不会用通货膨胀代替。
【讨论】:
好点,同意在代码中这样做更麻烦 @Karakur 很好的答案。我编写了一个小实验 github.com/FlyingPumba/AndroidLayoutsPerformanceBenchmark 并在我的手机(Moto G 1st gen)上进行了测试。通过 XML 在 LinerLayout 中加载具有 500 个 TextView 的活动平均需要 541 毫秒。以编程方式执行相同操作平均需要 380 毫秒。那是相当不同的。您能否解释一下为什么会这样?根据您的说法,我希望 XML 的视图加载速度更快。如果我将观看次数增加到 5000,这个差距会达到 1 秒。 我的想法是,即使 Inflater 使用双参数构造函数并且 xml 被优化,遍历树的整个过程也比你通过调用它节省的更“昂贵”特殊构造函数并避免双重调用,例如setBackground()
。
@FlyingPumba 使用反射调用构造函数(而不仅仅是使用new
)也可能会增加一些开销。但是,我认为其中很多内容确实没有实际意义:如果您要膨胀 500 或 5000(或更多)任何东西,您应该使用 RecyclerView
并回收视图。
@Karakuri 您是否尝试过同样使用 Kotlin Anko 布局?以上是关于Android - 布局性能:程序化 vs XML的主要内容,如果未能解决你的问题,请参考以下文章