kotlin-android-extensions插件可能算得上是我最喜欢的一个Kotlin在Android上的特性了。
这么说并不夸张,因为以前在使用Java开发Android程序时,我们总是要写一大堆的findViewById,枯燥又没什么意义。
虽然也有一些诸如ButterKnife之类的第三方库,专门用于对findViewById的用法进行简化,但是ButterKnife还是要通过注解来让控件与资源id之间进行绑定,并不算是非常方便。
而kotlin-android-extensions插件的出现则让这一情况完全发生了改变,我们可以不用再编写烦琐的findViewById代码,同时能用一种非常简便的写法进行替代。
比如说这里有一个布局文件activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/viewToShowText"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
非常简单,布局文件中只有一个TextView控件,它的id是viewToShowText。
那么,如果我想要在MainActivity中去设置TextView控件的内容,使用Java语言的话通常需要这样写:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView viewToShowText = findViewById(R.id.viewToShowText);
viewToShowText.setText("Hello");
}
}
可以看到,这里我们首先通过findViewById()函数获取到了TextView控件的实例,然后再调用setText()函数将其显示的内容设置成Hello。
这个findViewById()函数其实是很头疼的,这里我们只是获取了一个控件的实例,所以可能感受还不太明显。如果你要去获取10个甚至100个控件的实例,每个都要去findViewById一遍,你一定会抓狂的。
那么如果是使用Kotlin语言的话,这个问题要怎么解决呢?借助kotlin-android-extensions插件,我们可以使用如下代码来完成同样的功能:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewToShowText.text = "Hello"
}
}
可以看到,这里我们不再需要调用findViewById()函数去获取控件的实例,而是直接调用该控件在xml中定义的id名称,就能够设置其显示的内容了。
而这个神奇的功能就是由kotlin-android-extensions插件自动完成的,这个插件能够帮助我们减少大量琐碎无意义的代码。
/ 然而它被废弃了 /
其实早在几个月前,就有朋友在公众号上询问我,说自己升级了Android Studio 4.1之后,发现新建项目的时候Android Studio已经不会自动帮我们引入kotlin-android-extensions插件了,需要自己手动去添加才能使用,是不是Google不再推荐使用这个插件了?
当时我还说,不可能呀,这个插件这么好用,而且Kotlin也是Google未来主推的技术,可能只是Android Studio 4.1的bug吧。
然而,没过多久我就被打脸了。某天我将项目工程的Gradle版本升级到了最新,然后构建项目时发现了这样一个警告提示:
Google明确地告诉我们,kotlin-android-extensions插件已被废弃,现在推荐使用ViewBinding来进行替代。
对于Google的这种技术迭代频率我是有点生气的,如果kotlin-android-extensions插件是Google主推的技术,理应拥有更长的生命周期,不然的话就不该作为默认插件 集成到Android Studio当中。要知道,去年我才刚刚出版的新书《第一行代码 第3版》里还大量使用了这个技术。
不过,好在ViewBinding并不复杂,从kotlin-android-extensions插件切换到ViewBinding也是比较容易的,那么本篇文章就作为《第一行代码 第3版》的另外一篇DLC,向大家介绍一下,如何使用ViewBinding来替代kotlin-android-extensions插件。
/ 为什么会被废弃 /
在开始介绍ViewBinding之前,我还是想先讨论一下,为什么kotlin-android-extensions插件会被废弃。
虽说Google的技术迭代频率常常会让我们直呼学不动了,但是Google也绝对不会无缘无故去废弃一个之前主推的技术,说明kotlin-android-extensions插件肯定还是存在问题的。
那么到底存在什么问题呢?
比较容易让人想到的一个缺点就是,kotlin-android-extensions插件只能支持Kotlin语言,而无法支持Java语言。当然这个我认为并不是主要原因,因为现在Google开发的各种新技术都在全面兼容Kotlin,而不再怎么去考虑Java了,如协程、Jetpack Compose等。
那么主要原因是什么呢?这可能就要从kotlin-android-extensions插件的实现原理去理解了。刚才我们已经看到过了使用kotlin-android-extensions插件后的代码,非常简单:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewToShowText.text = "Hello"
}
}
那么这段代码为什么可以工作呢?
我们可以通过点击Android Studio顶部导航栏的Tools -> Kotlin -> Show Kotlin Bytecode来查看这段代码对应的Kotlin字节码,然后在弹出窗口中点击Decompile按钮将字节码反编译成Java代码。
为了方便阅读,我将反编译后的代码又做了些整理,大致如下所示:
public final class MainActivity extends AppCompatActivity {
private HashMap _$_findViewCache;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(1300023);
TextView var10000 = (TextView)this._$_findCachedViewById(id.textView);
var10000.setText((CharSequence)"Hello");
}
public View _$_findCachedViewById(int var1) {
if (this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findVie