“编译时生成代码”


ButterKnife使我们经常使用的一款View注入框架,使用方便,特点是ButterKnife使用的是编译时生成代码,而不是反射注入。那么我就有一个问题,ButterKnife究竟是如何在编译时生成代码的呢?这就是这篇文章的目的了。。。

先做一个例子我们来试着在编译时做一些操作。 介绍几个概念

1.APT

APT-annotation processing tool是在编译时,扫描和处理注解的一个构建工具,该工能由 javac 来实现,我们可以在 javac 编译时源代码额外生成 java 源代码(也可以是其它类型的文件)。关于如何处理注解我们需要了解AbstractProcessor这个类。Annotation Processor的实现,都需要继承自该类。

2.AbstractProcessor

AbstractProcessor 是 javac 扫描和处理注解的关键类。介绍两个关键的方法,

(1)public Set getSupportedAnnotationTypes()

制定需要处理哪些注解 ,返回值是注解全类名的Set。

(2)public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)

Processor的主方法,可以在该方法中处理注解,并生成Java代码,并可以通过RoundEnvironment获取到被注解的元素。

先创建三个Module

1.app (Android Module已存在)
2.api (Java Module存放我们的注解)
3.compiler (Java Module 存放我们的Processor)

声明插件依赖

声明插件android-apt,这个是Android Studio 与annotation processors结合的一个插件,在构建工程时将辅助javac执行processor, 在根目录的build.gradle添加:

  classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

api Module

定义注解

    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.TYPE)
    public @interface MyAnnotation {
    }

app module

在app的build.gradle:

  apply plugin: 'com.neenbedankt.android-apt'

  dependencies {
      ...
      compile project(':api')
      apt project(':compiler')
      ...
  }

使用api中定义的注解

    @MyAnnotation
    public class Bean {
    }

compiler module

在build.gradle:

  apply plugin: 'java'

  dependencies {
      compile fileTree(include: ['*.jar'], dir: 'libs')
      compile 'com.google.auto.service:auto-service:1.0-rc3'
      compile 'com.squareup:javapoet:1.8.0'
      compile project(':api')
  }

这里添加了三个依赖
1.api: 我们需要获取到我们定义的MyAnnotation注解
2.auto-service:用来自动生成 javax.annotation.processing.Processor 文件。location:https://github.com/google/auto/tree/master/service
3.javapoet:自动生成代码的工具类库。location:https://github.com/square/javapoet

然后创建我们的Processor类。

@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(MyAnnotation.class.getCanonicalName());
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        System.out.println("Message");
        return true;
    }
}

这时clean下Project 然后运行./gradlew assemble就可以看到输出的Message。

JavaPoet是一个用来生成.java源文件的Java API。



本作品由 Tony Zhang 创作,采用 CC BY-NC-SA 3.0 许可协议 进行许可。

Comments