微信公众号:关注可了解更多的Android知识,专注于移动领域。问题或建议,请公众号留言; 如果你觉得文章对你有帮助,欢迎赞赏[^1]
[TOC]
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
@Target(ElementType.TYPE) // 注解作用在类上 @Retention(RetentionPolicy.CLASS) public @interface Test { String path(); } 复制代码
build.gradle 配置
apply plugin: 'java-library' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) } sourceCompatibility = "1.7" targetCompatibility = "1.7" 复制代码
注意gradle的配置
apply plugin: 'java-library' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':annotationlib') implementation 'com.google.auto.service:auto-servic:1.0-rc4' implementation 'com.squareup:javapoet:1.11.1' } sourceCompatibility = "1.7" targetCompatibility = "1.7" 复制代码
com.google.auto.service:auto-service:1.0-rc4 是注册Processor的
com.squareup:javapoet:1.11.1 是生成class文件的第三方库
定义Processor类
@AutoService(Processor.class) //指定编译的Java版本 @SupportedSourceVersion(SourceVersion.RELEASE_7) //指定处理的注解 @SupportedAnnotationTypes({"demo.prim.com.annotationlib.Test"}) public class MyClass extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { // 创建方法 MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC,Modifier.STATIC) .addParameter(String[].class,"args") .addStatement("$T.out.println($S)",System.class,"Hello JavaPoet") .build(); //创建类 TypeSpec typeSpec = TypeSpec.classBuilder("HelloWord") .addModifiers(Modifier.PUBLIC,Modifier.FINAL) .addMethod(main) .build(); //创建Java文件 JavaFile file = JavaFile.builder("com.ecample.test", typeSpec) .build(); try { file.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } return true; } } 复制代码
上述代码会生成如下的类
public final class HelloWord { public static void main(String[] args) { System.out.println("Hello JavaPoet"); } } 复制代码
配置项目根目录的build.gradle dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } 复制代码
配置app的build.gradle apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' //... dependencies { //.. compile project(':annotation') apt project(':compiler') } 复制代码
编译使用
在随意一个类添加@Test注解 @Test(path = "main") public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } 复制代码
点击Android Studio的Rebuild Project 会生成如下文件:
这时便可以调用HelloWord类了。
通过apt来简化 textView = findViewById(R.id.textView); 。
一般的可以这样写:
public class ManagerFindByMainActivity { public void findById(MainActivity activity) { activity.textView = activity.findViewById(R.id.textView); } } 复制代码
上面一段代码,这样写和之前的没有区别,反而每有一个Activity都要新建一个类去找到ID。那么我们是否可以通过apt来动态生成这个类呢?
答案当然是可以的。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface DIActivity { } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface BYView { int value() default 0; } 复制代码
gradle 配置:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.google.auto.service:auto-service:1.0-rc4' implementation 'com.squareup:javapoet:1.11.1' implementation project(':lib-annotation') } sourceCompatibility = "1.7" targetCompatibility = "1.7" 复制代码
核心代码实现:
@AutoService(Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_7) @SupportedAnnotationTypes({Constance.DIACTIVITY}) public class ByProcessor extends AbstractProcessor { private Elements elementUtils; private Types typeUtils; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); elementUtils = processingEnvironment.getElementUtils(); typeUtils = processingEnvironment.getTypeUtils(); } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { if (set != null) { Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(DIActivity.class); if (elementsAnnotatedWith != null) {//获取设置DIActivity 注解的节点 //判断注解的节点是否为Activity TypeElement typeElement = elementUtils.getTypeElement(Constance.Activity); for (Element element : elementsAnnotatedWith) { TypeMirror typeMirror = element.asType();//获取注解节点的类的信息 DIActivity annotation = element.getAnnotation(DIActivity.class);//获取注解的信息 if (typeUtils.isSubtype(typeMirror, typeElement.asType())) {//注解在Activity的类上面 TypeElement classElement = (TypeElement) element;//获取节点的具体类型 //创建参数 Map<String,Class<? extends IRouteGroup>>> routes ParameterSpec altlas = ParameterSpec .builder(ClassName.get(typeMirror), "activity")//参数名 .build(); //创建方法 MethodSpec.Builder method = MethodSpec.methodBuilder ("findById") // .addAnnotation(Override.class) .addModifiers(PUBLIC, STATIC) .returns(TypeName.VOID) .addParameter(altlas); //创建函数体 //获取TypeElement的所有成员变量和成员方法 List<? extends Element> allMembers = elementUtils.getAllMembers(classElement);//?? //遍历成员变量 for (Element member : allMembers) { //找到被BYView注解的成员变量 BYView byView = member.getAnnotation(BYView.class); if (byView == null) { continue; } //构建函数体 method.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)", member.getSimpleName(),//注解节点变量的名称 ClassName.get(member.asType()).toString(),//注解节点变量的类型 byView.value()));//注解的值 } //创建类 TypeSpec typeSpec = TypeSpec.classBuilder("ManagerFindBy" + element.getSimpleName()) .addModifiers(PUBLIC, FINAL)//作用域 .addMethod(method.build())//添加方法 .build(); //创建Javaclass 文件 JavaFile javaFile = JavaFile.builder("com.prim.find.by", typeSpec).build(); try { javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } else { throw new IllegalArgumentException("@DIActivity must of Activity"); } } } return true; } return false; } } 复制代码
implementation project(':lib_annotation') annotationProcessor project(':lib_compiler') 复制代码
@DIActivity public class MainActivity extends AppCompatActivity { @BYView(R.id.textView) public TextView textView; @BYView(R.id.textView1) public TextView textView1; @BYView(R.id.textView2) public TextView textView2; @BYView(R.id.button) public Button button; } @DIActivity public class SencodActivity extends AppCompatActivity { @BYView(R.id.sencodText) public TextView sencodText; } 复制代码
然后RebuildProject,就会得到:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ManagerFindByMainActivity.findById(this); textView.setText("我是APT找到的"); textView1.setText("我是APT找到的 --> 1"); textView2.setText("我是APT找到的 --> 2"); button.setText("我被APT找到我要跳转"); } public void click(View view) { Intent intent = new Intent(this, SencodActivity.class); startActivity(intent); } 复制代码
项目地址
TypeElement:类
ExecutableElement:成员方法
VariableElement:成员变量
通过包名和类名获取TypeName TypeName targetClassName = ClassName.get(“PackageName”, “ClassName”);
通过Element获取TypeName TypeName type = TypeName.get(element.asType());
获取TypeElement的包名 String packageName = processingEnv.getElementUtils().getPackageOf(type).getQualifiedName().toString();
获取TypeElement的所有成员变量和成员方法 List<? extends Element> members = processingEnv.getElementUtils().getAllMembers(typeElement);
参考文章
Android的组件化专题:组件化配置
APT实战
路由框架原理
模块间的业务通信
下面的是我的公众号二维码,欢迎关注。
###赞赏 如果你觉得到Android研究院对你有帮助,欢迎赞赏,有你的支持,Android研究院一定会越来越好!