比较 data binding,view binding,Kotlin Synthetics和findViewById 到底要用哪一个?
最近随着Kotlin 1.4.20版本的发布,Android官方宣布将废弃Kotlin Extension的Gradle插件,并且在后续的Kotlin Release版本也不再发布更新了。
Android的Kotlin Extensions插件有两个非常棒的特性:
- Synthetic可以让不再用
findViewById
,用kotlinx.android.synthetic
绑定就能实现。 - Parcelize可以让你用
@Parcelize
的注解就能实现Parcelable而不用写模板代码。
随着这个插件被废弃,我们再也用不到这些特性了。不用担心Parcelize,因为它将作为一个独立的插件发布,叫kotlin-parcelize。但是Kotlin Synthetic真的是要永远说再见了。
现在全球所有的Android开发者都怀着沉重的心情,不得不跟Synthetic说在。但是回过头了,我们得思考一下,为什么一开始极力推荐Synthetic,现在又将它废弃了?现在不能用了,我们又该怎么办呢?
“Necessity is the mother of invention”
历史——findViewById
早些时候,为了拿到这些带ID的View,我们必须用findViewById
。这个方法看起来很简单,但是再开发的过程中确有很多问题。
这个函数通过遍历view的层级结构来找到指定ID的控件。如果找到就立马返回对应的view否则返回null
。让我们看一下这种方式的有哪些问题:
- 通过了解它的工作方式,我们可以认为这个方法的消耗比较大。开着会在运行时用到,尤其在早期版本的listview的adapter实现,性能表现很差。
- 如果布局里面没有对应的ID,如果不是空安全的话,会在运行时报一个空指针的异常。
- android API 26之前,返回的view需要手动类型转换,如果类型不安全,会导致类型转换异常。
- 大量的模板代码。你得给所有的view写相同的代码,做类型转换,然后复制给一个变量。
然后就出现了像Butter Knife这样的库来简化这个操作。Butter Knife解决了模板代码的问题,但是依然没解决类型安全和空安全的问题。还有就是它使用的是注解会有一丢丢影响打包速度。(注意:Butter Knife现在已经被废弃了。)
救星Kotlin Synthetics来了
随着2017年发布了Android Kotlin Extensions Gradle插件,Kotlin Synthetics来了。对每个布局文件,Kotlin Synthetics会自动创建包含view的类——就是那么简单。你只需要在你的gradle文件里面导入这个插件,view的所有引用变量都配置好了,你可以直接用。
它的内部还是通过findViewById
来实现的,只调用一次,然后都是缓存。
这个用起来很方便有趣啊,为什么现在被废弃了。
让我们来分析一下。
优点
- 没有模板代码——只需要在gradle文件里面配置好就行了。Kotlin Synthetics会自动为你创建类。
- 类型安全——所有的View都是从布局按照对应的类型拿到的。
缺点
- 可能是不是空安全的——虽然说应该是空安全的,因为只有在布局文件里面的才会自动生成。但是如果你有多个布局文件根据不同的配置,有的布局里面有对应的view,但是有的又没有。这种情况你就得去手动判空了。
- 会污染命名空间——不同的布局可能有相同ID的view,你可能会误将其他布局的文件导入了,那么运行的时候就会报空指针异常了。
- 只能Kotlin用——Kotlin Synthetics只能在Kotlin里面用。不支持Java。可能有的项目没有完全从Java迁移到Kotlin,那么Kotlin Synthetics就不同统一项目里面获取ViewID的方式了。
因为这些问题,Kotlin Synthetics现在被废弃了。但是它的替代者是谁呢?我们应该回到之前的findViewById的方式吗?还是有其他推荐的方式?
让我们看一下有哪些推荐的方式:
Data Binding
这篇文档里面提到:“Data Binding是一个通过声明式的方式而不是编程式的形式,让你布局文件里面的UI组件绑定到你APP的数据源的支持库。”
在APP模块下的build.gradle文件开启dataBinding的开关就能用起来了。
android {
...
buildFeatures {
dataBinding true
}
}
在布局文件里面开启data binding,需要手动添加的标签到布局文件里面。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.myapp.data.ViewModel" />
</data>
<ConstraintLayout... /> <!-- UI layout's root element -->
</layout>
Android studio 会自动给你的布局创建一个带Binding后缀的类。比如,你的布局文件名是activity_main.xml,那么Android会自动生成一个叫ActivityMainBinding
的类。
Data binding 非常抢到,有以下的几个特性:
- 布局绑定表达式
- 对数据对象的监听
- 绑定适配器
- 两种绑定方式
优点
- 空安全——就算我们有依赖配置有多个布局文件,有的布局有对应的view,而有的没有,可以通过添加@Nullable的注解来处理做到空安全。
- 类型安全——所有的view都是从布局文件里按类型拿到的。
- 支持Kotlin和Java
缺点
- 模板代码——每个布局文件都要手动添加的标签,才能支持data binding。
- 增加打包时间——因为data binding需要生成带view的类文件,以及他们的getter setter和计算表达式。
View Binding
简单的说,View Binding是一个简化的或者说是data binding的一部分。
两个的区别是view binding只是为了做view的引用而不做数据源和UI的绑定。
在APP模块的build.gradle文件开启viewBinding的开关就嫩使用View Binding了。
android {
...
buildFeatures {
viewBinding true
}
}
复制代码
对于ViewBinding,你不需要在你的布局文件里面声明任何东西。所有的布局文件默认都会生成一个带Binding后缀的类文件。
private lateinit var binding: ResultProfileBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }
复制代码
View Binding 的优势
- 空安全——同data binding
- 类型安全——同data binding
- 没有模板代码——跟data binding不同,所有的布局文件不需要像data binding那样添加任何标签。
- 对编译时间没影响——跟data binding 不同,对编译时间没负面影响。
- 支持Kotlin和Java。
结论
View binding和data binding都应该是推荐的方式。对于简单的用户场景,用view binding替换Kotlin Synthetics,如果你还在很原始的使用findViewById
,你更应该做迁移了。
如果你觉得data binding也很强大,你也可以使用data binding。
还有一种方式就是view binding和data binding都用。那样配置了标签的布局文件就是按data binding的方式工作,其他则是view binding。
参考
- Android Kotlin Gradle Extension Plugin is deprecated
- Learn more about View Binding
- Learn more about Data Binding
- Migrate from Kotlin Synthetics to View Binding
- New Features in Android Studio Preview