前言
视图对象绑定我理解的大致分为三个时代,第一石器时代:findViewByid;第二青铜时代:Butter Knife;第三后Butter Knife时代:ViewBinding与KAE。以下是我最近看到的一篇关于视图绑定方式对比介绍的文章《谁才是ButterKnife的终结者?ViewBinding与Kotlin-android-extension的选择》,感觉不错转载如下。
Google在Android Studio 3.6 Canary 11版本中正式推出视图绑定(View Binding),相对有findViewById或者Butter Knife等现有的视图访问方式更有优势,JakeWharton也因此宣布了Butter Knife的终结:
Kotlin-android-extension
使用Kotlin的同学都知道 Kotlin-android-extension 可以方便的进行视图访问,所以Kotlin开发中很少使用Butter knife。ViewBinding难道比KAE还强大吗,ViewBinding与KAE谁才能扛起Butter Knife后时代的大旗呢?
要找到答案就要比较一下两者的优劣,曾几何时看过一张表格,似乎ViewBinding比KAE更有优势:(Kotlin Synthetic:KAE, ???:ViewBinding)
KAE是基于 Kotlin Compiler Plugin + Intellij Idea Plugin 实现的,IDEA Plugin让我们可以在编辑器中使用语法糖进行试图访问,Compiler Plugin可以在编译期对语法糖脱糖。整个过程是一个编译期的的闭环,何来的编译安全问题呢?
我们知道KAE可以将layout文件中的id映射为View对象直接在Activity或Fragment中使用,但是无法保证layout是Activity/Fragment的当前视图
例如有两个layout :activity_main 与 activity_other
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/message_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/message_other"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
我们实现Activity如下:
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_other.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Application will crash because "message_other" doesn't exist in "activity_main"
message_other.text = "Hello!"
}
}
KAE会扫描res下所有layout中的id,作为代码自动补全的候选项,message_other的使用虽然可以通过编译,但是因为不是当前layout中的id,所以运行时会出错。
同样的例子如果使用ViewBinding则可以避免上述问题:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
//This code will never compile and the IDE shows you an error
binding.message_other.text = "Hello!"
}
}
上面表格中KAE编译安全的✘也正是指的这个问题。
Null safety
还有另一张表格也做了两者的对比,当然也是以突出ViewBinding为前提
Only reference ids from current layout 在上面的例子已经介绍过了。Alway null-safe该如何理解呢?
先看下ViewBinding的Null-safe
Null-safe for layouts defined in multiple configurations. View binding will detect if a view is only present in some configurations and create a @Nullable property.
当有多个configuration时,需要对应多个layout,此时可能出现为空的情况,ViewBinding会在编译期发现并添加@Nullable注解。
经过实测,KAE也可以做到这一点,当某个configuration的layout缺少时,KAE同样会给出提示:
需要进行可空处理:
所以在空安全方面,ViewBinding并不比KAE有优势。
How to choose?
啰嗦了这么多,到底应该如何选择呢?
首先客观的说ViewBinding比KAE的优势并不明显,都兼具了类型安全、空安全、编译速度等方面的优势(编译速度可能KAE更加,KotlinCompilerPlugin由于GradlePlugin),虽然KAE在编译安全上有缺陷,但只要开发时稍加注意也不是大问题,而且KAE的模板代码比起ViewBinding更少,从简洁性上说要更优秀,所以个人认为在Kotlin开发中,KAE与ViewBinding是不分伯仲的。
但是ViewBinding有一个优势KAE无法比拟,就是身份问题:
KAE来自JetBrains而非Google官方的,随着ViewBinding的诞生,相信Google会在未来的AOSP以及Jetpack中更多地推荐使用ViewBinding,例如下面某个commit的comment中已经可以看出端倪
kotlinx.android.synthetic is no longer a recommended practice
https://android-review.googlesource.com/c/platform/frameworks/support/+/882241
虽然JetBrains对于KAE来说已经是一种背书了,但是在Android世界中毕竟Google才是老大(参考Anko与Jetpack-KTX的现状),所以你心中的是否已经有答案了呢?