Konlin注解处理器——简易版ButterKnife实现
1. ButterKnife简介
ButterKnife是一个专注于Android系统的View注入框架,它通过在编译期生成class文件,为开发者自动完成findViewById方法的调用,对注解的View进行实例绑定。
ButterKnife最基本的使用方法分为4步:
1.在build.gradle中添加依赖
//Java中使用注解处理器不需要添加这个插件
//kotlin中使用注解处理器需要添加这个插件,否则只能识别java的注解,不能识别kotlin的注解
//kapt插件能够同时识别kotlin注解和java注解
apply plugin: 'kotlin-kapt'
implementation 'com.jakewharton:butterknife:10.1.0'
//kotlin中,添加注解处理器的依赖写法用annotationProcessor
//annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
//kotlin中,添加注解处理器的依赖写法用kapt
kapt 'com.jakewharton:butterknife-compiler:10.1.0'
2.对Activity中的View添加@BindView注解。
@BindView(R.id.tv)
lateinit var tv: TextView
3.在Activity的onCreate方法中调用setContentView之后,调用ButterKnife.bind(this)对所有的添加了注解的View进行实例绑定。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler_view)
ButterKnife.bind(this)
}
4.在Activity中无需调用findViewById方法对View进行赋值,即可直接使用。
tv.text = "Hello World!!!"
本文要实现的功能就是这样一个最基础的简易版ButterKnife。
2. 正文前的说明
- Kotlin提供的
kotlin-android-extensions插件已经提供了很方便的View自动绑定功能,所以使用Kotlin时是没必要使用ButterKnife的(个人观点)。 - 本文的目的是介绍和记录在kotlin中APT(Annotation Processing Tool,注解处理器)的使用方法,以及如何使用KotlinPoet自动生成kotlin代码,以及在这期间自己踩得一些坑,如果只关心代码实现,可以直接跳转到最后一章,或者查看源代码。
- 以下内容纯属个人见解结合网上资料完成。存在错误,实属正常,如有不足,欢迎指正。
3. 自动绑定View的原理
实现View的自动绑定需要3个类之间进行合作。
首先,Activity中提供带绑定的View,同时调用ButterKnife.bind(this)完成绑定。代码见ButterKnife简介中的第三代码段
其次,ButterKnife类中提供静态方法bind(activity:Activity)在该方法中通过反射实例化一个Binding类,同时传入activity作为实例化的参数,这个Binding类与具体传入的Activity类相关(即,一个Activity对应一个Binding类,Binding类的命名规则:ButterKnife_**_Binding,**为Activity的类名)。具体代码如下:
class ButterKnife {
companion object {
fun bind(activity: Activity) {
//Binding类的类名由具体的Activity的类名确定
val clazzName = "${
activity.javaClass.`package`.name}.ButterKnife_${
activity.javaClass.simpleName}_Binding"
val clazz = Class.forName(clazzName)
val constructor = clazz.getConstructor(activity.javaClass)
constructor.newInstance(activity)
}
}
}
最后,在Binding类的构造方法中利用activity实例调用findViewById()方法进行View的绑定。具体代码如下:
public class ButterKnife_MainActivity_Binding() {
public constructor(activity: MainActivity) : this() {
activity.tv=activity.findViewById(2131230814)
}
}
说明:
- 为什么ButterKnife的
bind()方法要用反射?因为每个Activity都有自己的Binding类,两者之间只有类名相关,反射调用Binding类的构造方法,在构造方法对View进行赋值,可以为所有的Activity提供统一的绑定View的方式。- 反射不消耗性能么?事实上只是通过反射调用构造方法,并没有反射遍历所有属性并分析注解这种耗时操作,和虚拟机构造一个类的实例差不多。
- 每个Activity对应一个
Binding类,命名还有要求,写代码不是变复杂了?事实上,Binding类是APT工具在编译期使用KotlinPoet自动生成的。ButterKnife类只有一个,并且写在一个单独的Module里,所以在使用时只需要在Activity中对应的View上打注解,然后调用ButterKnife.bind(this)即可。@BindView注解的作用是什么?辅助APT生成对用的Binding类。- 其他注意事项:
View不能是private,且要声明为lateinit var,不然在Binding类中无法赋值。
4. APT的使用
APT,即注解处理器。在Android中,使用gradle将源文件编译打包成Android的APK文件,事实上是执行了gradle插件中的一个个task,这些task负责完成不同的任务。下图(偷来的,点击查看原文)展示了Android的编译打包流程(缺少签名的过程),APT的工作与图中箭头所指的aapt类似(APT和aapt是两个东西),即APT会在task调用javac对源文件进行编译前被调用,根据APT的代码生成Generated Source Files,生成的代码会和其他的Source Files一起被javac编译成class文件,放进最终的apk中。

编写APT需要两个要素:注解和注解处理器,使用方法如下。
-
创建注解处理器对应的库:New->Module->Java or Kotlin Libray,填写库名和类名->Finish

说明:Module一定是Java or Kotlin Libiary,否则注解处理器无法生效。事实上,注解处理器确实不算Android Library,因为它是工作在编译期间的。库名和类名可以随意,但是后面会被用到。请忽略图中的报错,因为不想删库重新创建一遍。
-
在butterknife-annotation-lib/src/main目录下创建文件夹 resources/META-INF/services,并在services文件夹下创建文件javax.annotation.processing.Processor。这一步的每一个文件夹和文件的命名都是固定的,不能修改。最后在javax.annotation.processing.Processor文件中写注解处理器对应类的全限定名。本文中是:
com.cam.butterknife_compile_lib.ButterKnifeAnnotationProcessor


说明:此步配置是为了让gradle在编译前将注解处理器(
ButterKnifeAnnotationProcessor)识别出来并执行其中的代码。 -
创建注解类。步骤2类似,创建一个Java or Kotlin Libiary的模块,模块名随意,本文为
butterknife-annotation-lib。在模块中创建BindView注解。
BindView注解的代码为:@Retention(AnnotationRetention.SOURCE) @Target(AnnotationTarget.FIELD) annotation class BindView(val value:Int)说明:
@Retention(AnnotationRetention.SOURCE)说明注解只会存活在源码中,在编译阶段使用。@Target(AnnotationTarget.FIELD)说明注解使用在属性上的。value用来记录View的id。- 将
@BindView注解放在Java or Kotlin Libiary中,是因为注解处理器后面要读取这个注解,Activity也会使用这个注解,如果是Android的Module,注解处理器的类读取注解时会有问题。也可以将@BindView注解放在和注解处理器同一模块,但是那样Activity所在的模块添加依赖时就会很丑陋。
-
添加依赖。
在app模块的build.gradle添加对butterknife-compile-lib模块的依赖(注解处理器模块)、butterknife-annotation-lib模块的依赖(提供注解)、以及声明kapt插件。代码如下:plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' }dependencies { implementation project(path: ':butterknife-annotation-lib') kapt project(path: ':butterknife-compile-lib') }说明:注解处理器的依赖必须用
kapt,不能用annotationProcessor,否则无法识别打在Kotlin代码上的注解,只能识别打在Java代码上的注解。使用kapt插件,既能识别打在Java上的注解,也能识别打在Kotlin上的注解。butterknife-compile-lib模块添加对butterknife-annotation-lib模块的依赖,同时把Java的版本改成Java8(因为后面使用KotlinPoet对Java的版本有要求,不是Java8会报错)。为了方便,添加上KotlinPoet依赖(反正后面迟早要添加),代码如下:java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { implementation project(path: ':butterknife-annotation-lib') implementation "com.squareup:kotlinpoet:1.10.2" }最后把
butterknife-annotation-lib模块中的java版本也改成Java8,不改会怎样呢?没试过!懒得试!修改方法相同,不在赘诉。 -
修改注解处理器代码。编写步骤2中创建的
<ButterKnifeAnnotationProcessor的代码,使其继承AbstractProcessor并重写其中的方法,代码如下。
Kotlin 中实现简易ButterKnife:注解处理器与KotlinPoet实战

本文详述了在Kotlin环境中使用注解处理器(Annotation Processing Tool, APT)和KotlinPoet实现类似ButterKnife的自动绑定View功能。首先介绍了ButterKnife的基本使用,然后讲解了注解处理器的工作原理和配置,包括创建注解、使用APT以及KotlinPoet的使用。接着,通过编写注解处理器的process方法生成Kotlin代码,并在最终的Android应用中使用生成的类。文章还讨论了KotlinPoet生成Kotlin代码的示例,最后展示了如何整合所有组件,实现ButterKnife的自动绑定功能。
最低0.47元/天 解锁文章
1182

被折叠的 条评论
为什么被折叠?



