APT 详解APT 实战常用方法常用Element子类赞赏
apt为何如此重要呢?现今越来越多的第三方库使用了apt技术,Dagger2、ButterKnife、ARouter等,在编译时根据annotation生成相关的代码逻辑,动态的生成Java class文件给开发带来了很大的便利。
首先要懂得annotation (注解)相关的基础知识。
APT 的全称为:Annotation Processing Tool 可以解释为注解处理器, 它对源代码文件进行检测找出其中的Annotation,使用指定的Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
下面我们通过一个例子来讲解apt实现的原理。本例实现gradle版本是3.0.1
1@Target(ElementType.TYPE) // 注解作用在类上
2@Retention(RetentionPolicy.CLASS)
3public @interface Test {
4 String path();
5}
build.gradle 配置
1apply plugin: 'java-library'
2
3dependencies {
4 implementation fileTree(dir: 'libs', include: ['*.jar'])
5}
6
7sourceCompatibility = "1.7"
8targetCompatibility = "1.7"
注意gradle的配置
1apply plugin: 'java-library'
2
3dependencies {
4 implementation fileTree(dir: 'libs', include: ['*.jar'])
5 implementation project(':annotationlib')
6 implementation 'com.google.auto.service:auto-servic:1.0-rc4'
7 implementation 'com.squareup:javapoet:1.11.1'
8}
9
10sourceCompatibility = "1.7"
11targetCompatibility = "1.7"
com.google.auto.service:auto-service:1.0-rc4 是注册Processor的
com.squareup:javapoet:1.11.1 是生成class文件的第三方库
定义Processor类
1@AutoService(Processor.class)
2//指定编译的Java版本
3@SupportedSourceVersion(SourceVersion.RELEASE_7)
4//指定处理的注解
5@SupportedAnnotationTypes({"demo.prim.com.annotationlib.Test"})
6public class MyClass extends AbstractProcessor {
7 @Override
8 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
9 // 创建方法
10 MethodSpec main = MethodSpec.methodBuilder("main")
11 .addModifiers(Modifier.PUBLIC,Modifier.STATIC)
12 .addParameter(String[].class,"args")
13 .addStatement("$T.out.println($S)",System.class,"Hello JavaPoet")
14 .build();
15 //创建类
16 TypeSpec typeSpec = TypeSpec.classBuilder("HelloWord")
17 .addModifiers(Modifier.PUBLIC,Modifier.FINAL)
18 .addMethod(main)
19 .build();
20
21 //创建Java文件
22 JavaFile file = JavaFile.builder("com.ecample.test", typeSpec)
23 .build();
24
25 try {
26 file.writeTo(processingEnv.getFiler());
27 } catch (IOException e) {
28 e.printStackTrace();
29 }
30 return true;
31 }
32}
上述代码会生成如下的类
1public final class HelloWord {
2 public static void main(String[] args) {
3 System.out.println("Hello JavaPoet");
4 }
5}
1配置项目根目录的build.gradle
2dependencies {
3 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
4}
1配置app的build.gradle
2apply plugin: 'com.android.application'
3apply plugin: 'com.neenbedankt.android-apt'
4//...
5dependencies {
6 //..
7 compile project(':annotation')
8 apt project(':compiler')
9}
10
编译使用
1在随意一个类添加@Test注解
2
3@Test(path = "main")
4public class MainActivity extends AppCompatActivity {
5 @Override
6 protected void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8 setContentView(R.layout.activity_main);
9 }
10}
点击Android Studio的Rebuild Project 会生成如下文件:
image.png
这时便可以调用HelloWord类了。
通过apt来简化 textView = findViewById(R.id.textView);。
一般的可以这样写:
1public class ManagerFindByMainActivity {
2
3 public void findById(MainActivity activity) {
4 activity.textView = activity.findViewById(R.id.textView);
5 }
6}
上面一段代码,这样写和之前的没有区别,反而每有一个Activity都要新建一个类去找到ID。那么我们是否可以通过apt来动态生成这个类呢?
答案当然是可以的。
1@Target(ElementType.TYPE)
2@Retention(RetentionPolicy.CLASS)
3public @interface DIActivity {
4
5}
6
7@Target(ElementType.FIELD)
8@Retention(RetentionPolicy.RUNTIME)
9public @interface BYView {
10 int value() default 0;
11}
gradle 配置:
1dependencies {
2 implementation fileTree(dir: 'libs', include: ['*.jar'])
3 implementation 'com.google.auto.service:auto-service:1.0-rc4'
4 implementation 'com.squareup:javapoet:1.11.1'
5 implementation project(':lib-annotation')
6}
7
8sourceCompatibility = "1.7"
9targetCompatibility = "1.7"
核心代码实现:
1@AutoService(Processor.class)
2@SupportedSourceVersion(SourceVersion.RELEASE_7)
3@SupportedAnnotationTypes({Constance.DIACTIVITY})
4public class ByProcessor extends AbstractProcessor {
5
6 private Elements elementUtils;
7
8 private Types typeUtils;
9
10 @Override
11 public synchronized void init(ProcessingEnvironment processingEnvironment) {
12 super.init(processingEnvironment);
13 elementUtils = processingEnvironment.getElementUtils();
14 typeUtils = processingEnvironment.getTypeUtils();
15 }
16
17 @Override
18 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
19 if (set != null) {
20 Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(DIActivity.class);
21 if (elementsAnnotatedWith != null) {//获取设置DIActivity 注解的节点
22 //判断注解的节点是否为Activity
23 TypeElement typeElement = elementUtils.getTypeElement(Constance.Activity);
24 for (Element element : elementsAnnotatedWith) {
25
26 TypeMirror typeMirror = element.asType();//获取注解节点的类的信息
27 DIActivity annotation = element.getAnnotation(DIActivity.class);//获取注解的信息
28 if (typeUtils.isSubtype(typeMirror, typeElement.asType())) {//注解在Activity的类上面
29
30 TypeElement classElement = (TypeElement) element;//获取节点的具体类型
31
32 //创建参数 Map<String,Class<? extends IRouteGroup>>> routes
33 ParameterSpec altlas = ParameterSpec
34 .builder(ClassName.get(typeMirror), "activity")//参数名
35 .build();
36
37 //创建方法
38 MethodSpec.Builder method = MethodSpec.methodBuilder
39 ("findById")
40// .addAnnotation(Override.class)
41 .addModifiers(PUBLIC, STATIC)
42 .returns(TypeName.VOID)
43 .addParameter(altlas);
44
45 //创建函数体
46
47//获取TypeElement的所有成员变量和成员方法
48 List<? extends Element> allMembers = elementUtils.getAllMembers(classElement);//??
49
50 //遍历成员变量
51 for (Element member : allMembers) {
52 //找到被BYView注解的成员变量
53 BYView byView = member.getAnnotation(BYView.class);
54 if (byView == null) {
55 continue;
56 }
57 //构建函数体
58 method.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)",
59 member.getSimpleName(),//注解节点变量的名称
60 ClassName.get(member.asType()).toString(),//注解节点变量的类型
61 byView.value()));//注解的值
62 }
63
64 //创建类
65 TypeSpec typeSpec = TypeSpec.classBuilder("ManagerFindBy" + element.getSimpleName())
66 .addModifiers(PUBLIC, FINAL)//作用域
67 .addMethod(method.build())//添加方法
68 .build();
69
70 //创建Javaclass 文件
71
72 JavaFile javaFile = JavaFile.builder("com.prim.find.by", typeSpec).build();
73
74 try {
75 javaFile.writeTo(processingEnv.getFiler());
76 } catch (IOException e) {
77 e.printStackTrace();
78 }
79
80 } else {
81 throw new IllegalArgumentException("@DIActivity must of Activity");
82 }
83 }
84
85 }
86 return true;
87 }
88 return false;
89 }
90}
1implementation project(':lib_annotation')
2
3annotationProcessor project(':lib_compiler')
1@DIActivity
2public class MainActivity extends AppCompatActivity {
3
4 @BYView(R.id.textView)
5 public TextView textView;
6
7 @BYView(R.id.textView1)
8 public TextView textView1;
9
10 @BYView(R.id.textView2)
11 public TextView textView2;
12
13 @BYView(R.id.button)
14 public Button button;
15}
16
17@DIActivity
18public class SencodActivity extends AppCompatActivity {
19
20 @BYView(R.id.sencodText)
21 public TextView sencodText;
22}
然后RebuildProject,就会得到:
image.png
1@Override
2 protected void onCreate(Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4 setContentView(R.layout.activity_main);
5 ManagerFindByMainActivity.findById(this);
6 textView.setText("我是APT找到的");
7 textView1.setText("我是APT找到的 --> 1");
8 textView2.setText("我是APT找到的 --> 2");
9 button.setText("我被APT找到我要跳转");
10 }
11
12 public void click(View view) {
13 Intent intent = new Intent(this, SencodActivity.class);
14 startActivity(intent);
15 }
image.png
项目地址
参考文章
Android的组件化专题: 组件化配置
APT实战
路由框架原理
模块间的业务通信