注册
apt

Android APT实战学习技巧

简介


APT(Annotation Processing Tool)即注解处理器,在编译的时候可以处理注解然后搞一些事情,也可以在编译时生成一些文件之类的。ButterKnife和EventBus都使用了APT技术,如果不会APT技术就很难看懂这两个框架的源码。


作用


使用APT可以在编译时来处理编译时注解,生成额外的Java文件,有如下效果:



  • 可以达到减少重复代码手工编写的效果。


如ButterKnife,我们可以直接使用注解来减少findviewbyid这些代码,只需要通过注解表示是哪个id就够了。




  • 功能封装。将主要的功能逻辑封装起来,只保留注解调用。
  • 相对于使用Java反射来处理运行时注解,使用APT有着更加良好的性能。

Android基本编译流程


Android中的代码编译时需要经过:Java——>class ——> dex 流程,代码最终生成dex文件打入到APK包里面。


编译流程如图所示:




  • APT是在编译开始时就介入的,用来处理编译时注解。
  • AOP(Aspect Oridnted Programming)是在编译完成后生成dex文件之前,通过直接修改.class文件的方式,来对代码进行修改或添加逻辑。常用在在代码监控,代码修改,代码分析这些场景。

基本使用


基本使用流程主要包括如下几个步骤:



  1. 创建自定义注解
  2. 创建注解处理器,处理Java文件生成逻辑
  3. 封装一个供外部调用的API
  4. 项目中调用

整理思路



  1. 首先我们需要创建两个JavaLibrary
  2. 一个用来定义注解,一个用来扫描注解
  3. 获取到添加注解的成员变量名
  4. 动态生成类和方法用IO生成文件

实战


创建一个空项目



创建两个JavaLibrary



  • 注解的Lib: apt-annotation
  • 扫描注解的Lib: apt-processor



创建完之后



app模块依赖两个Library


implementation project(path: ':apt-annotation')
annotationProcessor project(path: ':apt-processor')


注解Lib中创建一个注解类


如果还不会自定义注解的同学,可以先去看我之前写的一篇Java自定义注解入门到实战


@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface Print {

}


扫描注解的Lib添加依赖


dependencies {
//自动注册,动态生成 META-INF/...文件
implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
//依赖apt-annotation
implementation project(path: ':apt-annotation')
}


创建扫描注解的类



重写init方法,输出Hello,APT


注意: 这里是JavaLib,所以不能使用Log打印,这里可以使用Java的println()或注解处理器给我们提供的方法,建议使用注解处理器给我们提供的



现在我们已经完成了APT的基本配置,现在我们可以build一下项目了,成败在此一举



如果你已经成功输出了文本,说明APT已经配置好,可以继续下一步了


继续完成功能


现在我们可以继续完成上面要实现的功能了,我们需要先来实现几个方法


/**
* 要扫描扫描的注解,可以添加多个
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> hashSet = new HashSet<>();
hashSet.add(Print.class.getCanonicalName());
return hashSet;
}

/**
* 编译版本,固定写法就可以
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}


定义注解


我们先在MianActivity中添加两个成员变量并使用我们定义的注解



定义注解


真正解析注解的地方是在process方法,我们先试试能不能拿到被注解的变量名


/**
* 扫描注解回调
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//拿到所有添加Print注解的成员变量
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Print.class);
for (Element element : elements) {
//拿到成员变量名
Name simpleName = element.getSimpleName();
//输出成员变量名
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,simpleName);
}
return false;
}


编译试一下



生成类


既然能拿到被注解的变量名,后面就简单了,我们只需要用字符串拼出来一个工具类,然后用IO流写到本地就ok了



查看效果


现在点击一下编译,然后我们可以看到app模块下的build文件已经有我们生成的类了



调用方法


现在我们回到MainActivity,就可以直接调用这个动态生成的类了



总结


优点:


它可以做任何你不想做的繁杂的工作,它可以帮你写任何你不想重复代码,将重复代码抽取出来,用AOP思想去编写。 它可以生成任何java代码供你在任何地方使用。


难点:


在于设计模式和解耦思想的灵活应用。在于代理类代码生成的繁琐:你可以手动进行字符串拼接,也可以用squareup公司的javapoet库来构建出任何你想要的java代码。


作者:Coolbreeze
链接:https://juejin.cn/post/7158450306680881160
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册