作者:Wajahat Karim
时间:2020/4/1
原文: Evolution of Finding Views by ID in Android
findViewById() vs Butterknife vs Kotlin Synthetics vs DataBinding vs ViewBindings
长久以来,Android开发人员一直在努力解决一个简单但烦人的问题。将XML布局中View的引用以完全独立的语言(Java或Kotlin)从其布局视图类(例如Activity或Fragments)中获取。
作为Android的初学者时,我曾感到很困惑。Android SDK提供了一个方法:findViewById()。功能方面,这个方法执行一个单一的任务——在XML布局中查找ID,返回一个View的引用。如果什么都没找到,它将返回NULL,据说它的创造者称其为十亿美元的错误。
在这篇文章中,我将探讨findViewById()如何演进和在“现代Android开发”中如何从XML布局中获取一个View的引用。
——————————————————————————————————
findViewById()方法
显然,首先是findViewById()方法。在API 1中引入,该方法需要一个ID作为参数,返回一个View对象。
这种方法存在一些问题。
- 如果在布局中存在该ID的View,则不会出现任何编译错误。相对地,当Android无法在Activity,Fragment,ViewGroup中找到该View时,将在运行时抛出NullPointerException。
- 如果该View在XML布局中是TextView,并且将其类型转换为Button,在编译期同样不会出错。但运行时会抛出ClassCastException,因为TextView无法转换为Button。
此方法被广泛使用,并且贯穿了Android SDK的整个发展过程。在compileSdk API为26时,略微修改了改方法以消除强制转换问题。
现在,开发人员无需在代码中手动强转View。如果您引用使用Button来引用TextView的ID,则Android SDK会尝试查找指定ID的Button,并且它将返回NULL,因为无法找到它。
但是在Kotlin中,您仍然需要提供一个View类型像这样:findViewById(R.id.txtUsername) 。这将抛出NullPointerException 如果你没有做空安全检查,但是这个方法不会像之前一样抛出ClassCastException。
分析
- 类型安全(Type-safe): 在API 26以前,非类型安全
- 空安全(Null-safe): 无。在使用之前必须进行空检查
- 样板代码(Boilerplate Code): 很多。你必须为每个在XML布局中使用到的View定义一个单独的变量
- 构建时间(Build Time): 无差异
- 语言(Languages): 支持Java和Kotlin
——————————————————————————————————
ButterKnife
很多库都试图通过不同的方法简化findViewById()的使用。特别地,Jake Wharton开源的Butterknife尤为出名并且引起了全球开发者的兴趣。它已经成了避开使用findViewById()的标准方式。
该库使用注解处理,并通过编译期生成的代码使用findViewById()方法从XML布局中获取View。它非常易于使用,并能有效减少开发人员的样板代码。
Butterknife具有与findViewById几乎相似的问题。然而,它在运行时添加空安全检查来避免NullPointerException。
注意: 现在不推荐使用此工具。请切换到view binding。当然,现有版本将继续维护,但是仅考虑与AGP集成相关的关键错误的修复。功能开发和常规错误的修复已停止。 ——原文:Butterknife Github repository
分析
- 类型安全(Type-safe): 非类型安全,因为使用了findViewById
- 空安全(Null-safe): 该库运行时在view访问前做了空判断
- 样板代码(Boilerplate Code): 通过注解处理器生成代码来减少样板代码
- 构建时间(Build Time): 构建时间受注解处理影响
- 语言(Languages): 支持Java和Kotlin
——————————————————————————————————
Kotlin Synthetics
Android SDK现在是Kotlin优先(Kotlin-first)。这意味着Android SDK将首先从Kotlin的角度实现API,然后再为Java添加这些API。
Kotlin引入的最大功能之一是Kotlin扩展方法(Kotlin Extension Methods)。在它的帮助下,Kotlin Synthetics诞生了。Kotlin Synthetics通过自动生成的Kotlin扩展方法,使开发人员可以从XML布局直接访问其视图。
Kotlin Synthetics调用一次findViewById方法,然后默认情况下将view实例缓存在HashMap中。可以通过Gradle设置将此缓存配置更改为SparseArray或不缓存。
总体而言,Kotlin Synthetics是一种很好的选择,因为它是类型安全的,并且需要通过Kotlin的“?”操作符进行空检查如果view仅在一些布局配置中存在。它不需要开发人员编写额外的代码。但这仅适用于Kotlin项目。
但是,许多开发人员在使用Kotlin Synthetics时遇到了一个小问题。例如,如果您将布局设置为content view,但输入仅在其他布局中存在的ID,IDE将为您自动补全并添加新的import语句。除非开发人员专门检查以确保其import语句仅导入正确的视图,否则没有安全的方法来验证这不会引起运行时问题。
分析
- 空安全(Null-safe): 通常情况下,它是空安全的。但是,如果视图ID在其他布局中也存在/缺失,那么需要开发人员显示的使用操作符"?"或者在使用前判空。开发人员可能使用了其他布局中的view导致NullPointerException
- 样板代码(Boilerplate Code): 没有样板代码因为扩展方法生成了。只需要在build.gradle中使用android-kotlin-extension插件。
- 语言(Languages): 仅支持Kotlin
——————————————————————————————————
Data Binding
Data Binding库是一个Support库,使您可以使用声明式样式(而不是通过编程方式)将布局中的UI元素绑定到应用程序中的数据源。
Data Binding在功能上比其他方法优越得多,因为它不仅为您提供类型安全和null安全的视图引用,而且还允许您直接在XML布局内使用视图映射数据。
您必须手动转换布局来支持Data Binding通过嵌套在标签中。
使用Data Binding最大的优点是,它使开发人员甚至无需访问Java / Kotlin文件中的视图即可将数据与XML中的视图进行映射。您也可以使用双向绑定来更新XML或数据值,而无需任何listener或callback。
分析
- 样板代码(Boilerplate Code): 要求每个布局文件都嵌套在标签内。而且,您还必须创建自动生成的绑定类的实例,并将其填充到您的Activity或Fragment中。
- 构建时间(Build Time): 由于生成布局对应的类文件而增加了构建时间。通常,它很慢,因为您必须手动点击“Make”按钮来为布局更新/生成新的类。
- 语言(Languages): 支持Java和Kotlin
——————————————————————————————————
View Binding
最近,ViewBinding作为Data Binding库的排序子集在Android Studio 3.6中引入。由于不需要注解处理,因此可以缩短构建时间。它不需要因为嵌套标签而导致过多关注布局文件,默认情况下,它适用所有布局文件。
它与Data Binding唯一的区别在于,该绑定仅用于视图引用。它不执行任何数据映射或双向数据绑定。
如果您只是在寻找一种从布局文件获取视图的好方法,则可以使用ViewBinding代替其他选项。
这仅适用于Android Studio 3.6或更高版本。您可以通过将其添加到app的build.gradle文件中来启用它。
然后,Android Studio将为您的所有布局文件生成View Binding类。您可以使用这些类在“Activity”或“Fragment”中进行填充,就像在Data Binding中所做的那样。
如果要跳过某些布局文件,可以通过在布局文件中添加 *tools:viewBindingIgnore =“ true”*来实现。
分析
- 样板代码(Boilerplate Code): 没有样板代码,因为View Binding类是自动生成的。它只需要在build.gradle中启用View Binding。
- 构建时间(Build Time): 对构建速度没有影响。 View Binding 旨在解决使用Data Binding 相关的性能问题,因此不会对构建速度产生负面影响。
- 语言(Languages): 支持Java和Kotlin
——————————————————————————————————
是时候做决定了
观察了所有方法及其分析,View Binding是目前可使用的最佳方法。