一小时搞明白注解处理器(AnnotationProcessor Tool)什么是注解处理器?注解处理器是(Annotation Processor)是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。
你可以自己定义注解和注解处理器去搞一些事情。
一个注解处理器它以Java代码或者(编译过的字节码)作为输入,生成文件(通常是java文件)。
这些生成的java文件不能修改,并且会同其手动编写的java代码一样会被javac编译。
看到这里加上之前理解,应该明白大概的过程了,就是把标记了注解的类,变量等作为输入内容,经过注解处理器处理,生成想要生成的java代码。
处理器AbstractProcessor处理器的写法有固定的套路,继承AbstractProcessor。
如下:[java] view plain copy 在CODE上查看代码片派生到我的代码片public class MyProcessor extends AbstractProcessor {@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);}@Overridepublic Set<String> getSupportedAnnotationTypes() {return null;}@Overridepublic SourceVersion getSupportedSourceVersion() {return testSupported();}@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {return true;}}init(ProcessingEnvironment processingEnv) 被注解处理工具调用,参数ProcessingEnvironment 提供了Element,Filer,Messager等工具getSupportedAnnotationTypes() 指定注解处理器是注册给那一个注解的,它是一个字符串的集合,意味着可以支持多个类型的注解,并且字符串是合法全名。
getSupportedSourceVersion 指定Java版本process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) 这个也是最主要的,在这里扫描和处理你的注解并生成Java代码,信息都在参数RoundEnvironment 里了,后面会介绍。
在Java7 中还可以使用[java] view plain copy 在CODE上查看代码片派生到我的代码片@SupportedSourceVersion(testSupported())@SupportedAnnotationTypes({// 合法注解全名的集合})代替getSupportedSourceVersion() 和getSupportedAnnotationType() ,没毛病,还可以在注解处理离器中使用注解。
注册注解处理器打包注解处理器的时候需要一个特殊的文件javax.annotation.processing.Processor 在META-INF/services 路径下[plain] view plain copy 在CODE上查看代码片派生到我的代码片--myprcessor.jar----com------example--------MyProcessor.class----META-INF------services--------javax.annotation.processing.Processor打包进javax.annotation.processing.Processor的内容是处理器的合法全称,多个处理器之间换行。
[plain] view plain copy 在CODE上查看代码片派生到我的代码片com.example.myprocess.MyProcessorAcom.example.myprocess.MyProcessorBgoogle提供了一个注册处理器的库[plain] view plain copy 在CODE上查看代码片派生到我的代码片compile 'com.google.auto.service:auto-service:1.0-rc2'一个注解搞定:[java] view plain copy 在CODE上查看代码片派生到我的代码片@AutoService(Processor.class)public class MyProcessor extends AbstractProcessor {...}读到这里ButterKnife用到的知识点我们都已经了解了1.自定义注解2.用注解处理器解析注解3.解析完成后生成Java文件BufferKnife使用:[java] view plain copy 在CODE上查看代码片派生到我的代码片public class MainActivity extends AppCompatActivity {@Bind(R.id.rxjava_demo)Button mRxJavaDemo;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);ButterKnife.bind(this);mRxJavaDemo.setText("Text");}}然后我们编译一下,打开路径:/app/build/intermediates/classes/release/com/ming/rxdemo/MainActivity$$ViewBinder.class这就是我们生成的Java文件,可以看到Button已经在bind里面初始化了。
[java] view plain copy 在CODE上查看代码片派生到我的代码片public class MainActivity$$ViewBinder<T extends MainActivity> implements ViewBinder<T> { public MainActivity$$ViewBinder() {}public void bind(Finder finder, T target, Object source) {View view = (View)finder.findRequiredView(source, 2131492944, "field \'mRxJavaDemo\'");target.mRxJavaDemo = (Button)finder.castView(view, 2131492944, "field \'mRxJavaDemo\'");}public void unbind(T target) {target.mRxJavaDemo = null;}}接下来我们创建一个项目,写一个简单的用注解绑定控件的例子项目结构[plain] view plain copy 在CODE上查看代码片派生到我的代码片--apt-demo----bindview-annotation(Java Library)----bindview-api(Android Library)----bindview-compiler(Java Library)----app(Android App)bindview-annotation 注解声明bindview-api 调用Android SDK APIbindview-compiler 注解处理器相关app 测试App1.在bindview-annotation 下创建一个@BindView注解,该注解返回一个值,整型,名字为value,用来表示控件ID。
[java] view plain copy 在CODE上查看代码片派生到我的代码片@Target(ElementType.FIELD)@Retention(RetentionPolicy.CLASS)public @interface BindView {/*** 用来装id** @return*/int value();}2.在bindview-compiler 中创建注解处理器BindViewProcessor 并注册,做基本的初始化工作。
[java] view plain copy 在CODE上查看代码片派生到我的代码片@AutoService(Processor.class)public class BindViewProcessor extends AbstractProcessor {/*** 文件相关的辅助类*/private Filer mFiler;/*** 元素相关的辅助类*/private Elements mElementUtils;/*** 日志相关的辅助类*/private Messager mMessager;/*** 解析的目标注解集合*/private Map<String, AnnotatedClass> mAnnotatedClassMap = new HashMap<>();@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);mElementUtils = processingEnv.getElementUtils();mMessager = processingEnv.getMessager();mFiler = processingEnv.getFiler();}@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> pes = new LinkedHashSet<>();types.add(BindView.class.getCanonicalName());//返回该注解处理器支持的注解集合return types;}@Overridepublic SourceVersion getSupportedSourceVersion() {return testSupported();}@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {return true;}}是不是注意到了里面有个Map容器,而且类型是AnnotatedClass,这是干啥的呢?这个很好理解,我们在解析XML,解析Json的时候数据解析完之后是不是要以对象的形式表示出来,这里也一样,@BindView用来标记类成员,一个类下可以有多个成员,好比一个Activity 中可以有多个控件,一个容器下有多个控件等。