写在前面
目前越来越多的框架使用编译时的注解,比如butterknife,Dagger2,EventBus等,下面以Butterknife为例一步一步的实现编译时的注解,算是高仿butterknife吧,如果想学习编译时的注解的话,不妨看下去,也许能帮助到你,如果你已经了解了的话就没必要看下去了。本系列将会由浅入深,可以说是从入门到精通吧,当你了解了的话,你会发现其实编译时的注解也没什么,只是不太熟悉其api而已,接下来介绍如何搭建项目,介绍api的使用,然后打造一个butterknife。准备拆分3篇来讲,这一篇主要是项目的搭建以及如何来debug
项目moudle之间的依赖关系如下图
依赖关系.png
注:其中butterKnife-compiler、butterKnife-annoation为java lib
- butterKnife-annoation:专门存放注解的module
- butterKnife-compiler : 专门处理注解的module
- butterKnife-core: 使用编译生成的代码并提供api供上层使用
创建butterKnife-annoation
其build.gradle
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
创建butterKnife-compiler
其build.gradle
apply plugin: 'java-library'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':butterKnife-annoation')
implementation 'com.google.auto.service:auto-service:1.0-rc2'
implementation 'com.squareup:javapoet:1.8.0'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
创建butterKnife-core
其build.gradle中的依赖
dependencies {
...
api project(':butterKnife-annoation')
}
app module中依赖
dependencies {
...
implementation project(':butterknife-core')
annotationProcessor project(':butterKnife-compiler')
}
项目创建完了之后的目录结构为
项目目录结构
其中2个注解如下,不懂注解的同学,请自行百度,这里就不作介绍了,这里butterknife-core中的类这里可以忽视,后面需要用到时再讲
/**
* Created by cool on 2018/7/3.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnClick {
int[] value();
}
/**
* Created by cool on 2018/7/1.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface BindView {
int value();
}
下面来到了我们的重点BindViewProcessor,将详细介绍这个类
BindViewProcessor.java
@AutoService(Processor.class)//自动注册
@SupportedAnnotationTypes("com.cool.butterknife.annoation.BindView")//指名要处理的注解
@SupportedSourceVersion(SourceVersion.RELEASE_7)//知名支持的java版本
public class BindViewProcessor extends AbstractProcessor {
private Messager messager;//打印日志的类
private Filer filer;
private Elements elementUtils;//元素节点工具
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
messager = processingEnvironment.getMessager();
elementUtils = processingEnvironment.getElementUtils();
messager.printMessage(Diagnostic.Kind.NOTE, "=====init=====");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
messager.printMessage(Diagnostic.Kind.NOTE, "=====process=====");
return false;
}
}
解释一下上面涉及到的注解:
- @AutoService 这个注解能帮助我们自动注册,对应
com.google.auto.service:auto-service:1.0-rc2
如果不使用这个注解的话,需要手动去注册,步骤是,在butterKnife-compiler
module的main目录下创建resources/META-INF/services
文件夹,再创建javax.annotation.processing.Processor文件,文件中写BindViewProcessor的全类名
如下图:
image.png - @SupportedAnnotationTypes 能处理的注解类型,里面接受的是一个注解,说明可以同时处理多个,butterknife就是支持多个,同时处理BindView和OnClick注解,如果不使用注解的话,你也可以重写
getSupportedAnnotationTypes
方法,getSupportedAnnotationTypes
返回的是一个set<String>集合,里面添加需要处理的集合信息
如:
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = new HashSet<>();
set.add("com.cool.butterknife.annoation.BindView");
return set;
}
- @SupportedSourceVersion 支持的jdk版本,一般返回支持的最新的
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
重写方法或着使用注解达到的效果是一致的,怎样使用看个人习惯了
设置断点调试
logcat在这里就不再适用了,想打日志的话就只能用里面的Messager类了,需要在gradle控制台上查看输出的日志,但是只有日志的话还是远远不够的,这时我们需要像调试java程序一样能设置断点,这样能只管的看到程序运行时的状态,方便找错误,下面是设置断点调试的步骤
1. 在工具栏run中找到Edit Configurations,点击打开
2CF280D9-C7A5-4098-BB83-C960E3BDE192.png
2.点击+号添加Remote
9A0EBE91-6713-4858-A371-D5C2D76D719C.png
3.复制红框中的参数点击ok
B3101456-9C90-4378-A6E0-B223D206DC12.png
4.打开gradle,找到红框中的条目点击打开
1BDD0EC9-A6A4-4344-B501-BC88A7BDF4AA.png
5.将前面复制的参数粘贴到VM options,并将suspend=n改成suspend=y,再点击确定
image.png
6.选上之前创建的Remote,再debug运行,这时发现debug的红点亮了,而且代码也没有执行编译,这是按第7步操作(debug运行前先在代码中设置好断点,并且clean一下项目)
image.png
7.打开gradle找到红框中的双击
image.png
8.成功debug,如果不能成功debug,尝试先clean项目,如果还不行的话改一下端口,将5005改成别的端口,可能5005端口被别的占用了,或者点击debug右边的正方形红点,让其停止运行
image.png
使用annotationProcessor打造编译时的注解(二)
使用annotationProcessor打造编译时的注解(三)