对AOP 开发完全不了解的同学,请移步到 Android AOP 理解(一)
OK 今天我们来讲下AOP 中的APT 开发,通过上一遍我们已经知道目前市面上比较流行的APT 框架有
ButterKnife、Dagger2、DBFlow、AndroidAnnotation、EventBus
其中EventBus 3.x发布之后其通过注解预编译的方式解决了之前通过反射机制所引起的性能效率问题,其中注解预编译所采用的的就是
android-apt的方式,不过Apt工具的作者宣布了不再维护该工具了,因为Android Studio推出了官方插件,并且可以通过gradle来简
单的配置,它就是annotationProcessor,所以我们采用google官方推荐的方案来开发我们的APT项目。
在开始之前,我们要知道什么是APT:
APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
有了这个想法之后,我们来写个简单的栗子,比如我们想让它自动生成如下代码:
package com.example.helloworld;
import java.lang.String;
import java.lang.System;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
创建Annotation Module
首先,我们需要新建一个名称为annotation的Java Library(注意为什么这里创建Android Lib 是因为android默认不支持javax 包。这个包在我们complier中需要用到。所以这里我们暂且都采用创建 java Lib
),主要放置一些项目中需要使用到的Annotation和关联代码。这里简单自定义了一个注解:
package com.apt.andy.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by Administrator on 2018/1/24 0024.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface TestAnnotation {
}
配置gradle文件:
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
创建Compiler Module
创建一个名为compiler的Java Library,注意,这个是我们的核心,我们自动生成的代码需要在这里编写
在这个module中我们新建一个TestProcessor 继承于AbstactProcessor(这个类具体干嘛的后面我们再说)
代码如下:
@AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(TestAnnotation.class.getCanonicalName());
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
TestProcessor中重写了AbstactProcessor中的4个方法,这4个方法的具体作用是什么?
1.init(ProcessingEnvironment processingEnv)
所有的注解处理器类都必须有一个无参构造函数。然而,有一个特殊的方法init(),它会被注解处理工具调用,以ProcessingEnvironment作为参 数。ProcessingEnvironment 提供了一些实用的工具类Elements, Types和Filer。
2.process(Set<? extends TypeElement> annoations, RoundEnvironment env)
主要的逻辑处理都在这里,这个方法里面可以实现扫描,处理注解,生成 java 文件。使用RoundEnvironment 参数,可以查询被特定注解标注的元 素。
3.getSupportedAnnotationTypes()
在这个方法里面你必须指定哪些注解应该被注解处理器注册。注意,它的返回值是一个String集合,包含了你的注解处理器想要处理的注解类型的全称
4. getSupportedSourceVersion()
用来指定支持的java版本
在Java 7后多了 SupportedAnnotationTypes 和 SupportedSourceVersion 这个两个注解用来简化指定注解和java版本的操作:
也就是说可以改成这样:
@SupportedAnnotationTypes({"com.apt.andy.annotation.TestAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
其中
SupportedAnnotationTypes 如果支持多个注解,中间用逗号隔开
gradle 配置如下:
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.squareup:javapoet:1.7.0'
compile 'com.google.auto.service:auto-service:1.0-rc2'
compile project(':annotation')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
然后在App中直接用注解就可以:
package com.apt.andy.myfirstaptdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.apt.andy.annotation.TestAnnotation;
@TestAnnotation
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
App gradle配置如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.apt.andy.myfirstaptdemo"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
compile project(':annotation')
annotationProcessor project(':compile')
}
然后Rebuild下,就可以在App build/generated/source/apt/debug 目录中看到我们要生成的类了。