2023年6月20日发(作者:)

androidstudio编译时注解(⼀)⼯作原理解析本教程是基于android studio 3.0+来实践的

为什么要⽤编译时注解?因为运⾏时注解是⼗分消耗资源的,Eventbus的低版本就是采⽤运⾏时注解,被吐槽效率低下,所以新版本的Eventbus都是采⽤编译时注解注⼊的,其效率基本和直接写代码没什么区别在整理思路之前,我们来了解⼀下,编译时注解和运⾏时注解运⾏时注解通常被定义的注解需要通过反射来获取相关值编译时注解在代码构建编译过程的时候,⽣成java⽂件然后供需要的类进⾏调⽤两者根本区别在于,前者是程序员预先写好的java⽂件中,直接调⽤的,⽽后者是程序员写好java代码的⽣成规则,程序员⾃⼰不写java⽂件,交给编译器去写java⽂件,,java⽂件只有编译器编译完成后才能调⽤.图解⼯程第⼀点app主⼯程

中需要依赖tion,并且指定⼀个编译时注解器dependencies {

annotationProcessor project(':s') //指定注解器(图中的3) implementation project(':tion') //依赖注解类⼯程(图中2)}解析:当gradle运⾏编译的时候,会进⼊到s⼯程中,对整个app⼯程使⽤了tion⼯程的java⽂件进⾏检索,结果会在s⽅法中回调,最后process结束后将编译出来的java代码输⼊到app⼯程的.*中,到此为⽌,编译出来的java代码就跟⾃⼰创建的java⽂件⼀样调⽤第⼆点tion是⼀个java library⼯程,,注意,是java library⼯程,不是model

主要是负责定义注解类,图中可见,定义了2个注解类第三点(重点)⽤于编译时注解的⼯程,也是⼀个java library⼯程,因为需要⽤到注解类,所以需要依赖tion

配置如下dependencies {

implementation 'up:javapoet:1.9.0' //避免徒⼿写java代码的裤⼦ implementation project (':tion') //依赖注解⼯程}HzcInjectProcess类是主要⼯作的类,由它负责实现AbstractProcessor的process的⽅法.就是上⽅第⼀点说的检索回调

其他的两个⽂件都不是什么重要⽂件,主要是⽤些编写⼀些业务逻辑⽤的第四点如果知道SPI的⼈应该知道这个是什么,,其实这个就是以SPI的模式进⾏⼯作的,需要实现类为

sor

如果不懂什么是SPI,就按照⼯程中的模样直接敲就好了,,固定的

⽂件⾥就写上你实现了Processor类的完整路径,在这个项⽬⾥,是这样的

ectProcess现在我们重点来看HzcInjectProcess类@SupportedAnnotationTypes({"e", "re"}) //告诉这个实现类,需要监听检索哪些注解@SupportedSourceVersion(E_7)//最低⽀持的源码版本,这⾥是java7public class HzcInjectProcess extends AbstractProcessor { //获得⼀些代码输出成⽂件的⼯具类 ProcessingEnvironment processingEnvironment; public static final String PACKAGE_NAME = ""; public static final String CLASS_NAME = "HzcInject"; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { (processingEnvironment); //赋值代码输出成⽂件的⼯具类 singEnvironment = processingEnvironment; } /** * 主要检索回调的⼊⼝,所以编译动作从这⾥开始 */ @Override public boolean process(Set set, RoundEnvironment roundEnvironment) { List javaFiles = new ArrayList<>(); List serviceList = new ArrayList<>(); //得到所有⽤到Service注解的元素 for (Element p : mentsAnnotatedWith()) { try { (ng()); * RoundEnvironment 包含了检索的结果,也就是说,哪些类⽤了上⾯SupportedAnnotationTypes中指定的注解类,以及相关可操作的值,反正⼀切和注解有关的都 (ng()); } catch (Exception e) { } } //havapoet⽣成java⽂件,不熟悉javapoet的同学⾃⼰查资料吧 TypeSpec hzcInject = uilder(CLASS_NAME) .superclass((, , )) .addModifiers(, ) .addMethod(Constructor()) .addMethod(eInject()) .addMethod(StaticInstance()) .addType(InnerClass()) .addMethod(InitService(serviceList)).build(); (r(PACKAGE_NAME, hzcInject).build()); Map> activitys = new HashMap<>(); for (Element p : mentsAnnotatedWith()) { String key = losingElement().toString(); List elementList = (key); if (elementList == null) elementList = new ArrayList<>(); (p); (key, elementList); } for (String key : ()) { List elementList = (key); (r(PACKAGE_NAME + ".model", InjectClass(key, elementList)).build()); } int size = (); for (int i = 0; i < size; i++) { try { (i).writeTo(er()); } catch (IOException e) { } } //告诉处理器,,处理完成了 return true; }}解析:创建⼀个类去实现ctProcessor,同时将这个类的完整包路径写⼊到sor⽂件中.

在类的头部告诉处理器需要处理检索的注解类,并制定编译版本,然后在process中对检索的结果进⾏处理.

mentsAnnotatedWith()//获得所有使⽤了这个注解的对象

ng //获得当前使⽤了注解的对象的完整包类名,如果作⽤在属性,那就是属性名,如果作⽤在类

那么就是类名

losingElement().toString()//当作⽤在属性的时候,获得这个属性所在的类全称

otationMirrors()//获得元素的所有注解

otationMirrors().get(0).getElementValues()//获得注解的值Map对象

pleName()//获得使⽤了注解的类/属性的名称

()//获得使⽤了注解的类/属性的类型到此,完成⼀个编译时注解基本流程,剩下的⼯作就是,把原来运⾏时注解的代码⽤javapoet进⾏翻译就可以了.

2023年6月20日发(作者:)

androidstudio编译时注解(⼀)⼯作原理解析本教程是基于android studio 3.0+来实践的

为什么要⽤编译时注解?因为运⾏时注解是⼗分消耗资源的,Eventbus的低版本就是采⽤运⾏时注解,被吐槽效率低下,所以新版本的Eventbus都是采⽤编译时注解注⼊的,其效率基本和直接写代码没什么区别在整理思路之前,我们来了解⼀下,编译时注解和运⾏时注解运⾏时注解通常被定义的注解需要通过反射来获取相关值编译时注解在代码构建编译过程的时候,⽣成java⽂件然后供需要的类进⾏调⽤两者根本区别在于,前者是程序员预先写好的java⽂件中,直接调⽤的,⽽后者是程序员写好java代码的⽣成规则,程序员⾃⼰不写java⽂件,交给编译器去写java⽂件,,java⽂件只有编译器编译完成后才能调⽤.图解⼯程第⼀点app主⼯程

中需要依赖tion,并且指定⼀个编译时注解器dependencies {

annotationProcessor project(':s') //指定注解器(图中的3) implementation project(':tion') //依赖注解类⼯程(图中2)}解析:当gradle运⾏编译的时候,会进⼊到s⼯程中,对整个app⼯程使⽤了tion⼯程的java⽂件进⾏检索,结果会在s⽅法中回调,最后process结束后将编译出来的java代码输⼊到app⼯程的.*中,到此为⽌,编译出来的java代码就跟⾃⼰创建的java⽂件⼀样调⽤第⼆点tion是⼀个java library⼯程,,注意,是java library⼯程,不是model

主要是负责定义注解类,图中可见,定义了2个注解类第三点(重点)⽤于编译时注解的⼯程,也是⼀个java library⼯程,因为需要⽤到注解类,所以需要依赖tion

配置如下dependencies {

implementation 'up:javapoet:1.9.0' //避免徒⼿写java代码的裤⼦ implementation project (':tion') //依赖注解⼯程}HzcInjectProcess类是主要⼯作的类,由它负责实现AbstractProcessor的process的⽅法.就是上⽅第⼀点说的检索回调

其他的两个⽂件都不是什么重要⽂件,主要是⽤些编写⼀些业务逻辑⽤的第四点如果知道SPI的⼈应该知道这个是什么,,其实这个就是以SPI的模式进⾏⼯作的,需要实现类为

sor

如果不懂什么是SPI,就按照⼯程中的模样直接敲就好了,,固定的

⽂件⾥就写上你实现了Processor类的完整路径,在这个项⽬⾥,是这样的

ectProcess现在我们重点来看HzcInjectProcess类@SupportedAnnotationTypes({"e", "re"}) //告诉这个实现类,需要监听检索哪些注解@SupportedSourceVersion(E_7)//最低⽀持的源码版本,这⾥是java7public class HzcInjectProcess extends AbstractProcessor { //获得⼀些代码输出成⽂件的⼯具类 ProcessingEnvironment processingEnvironment; public static final String PACKAGE_NAME = ""; public static final String CLASS_NAME = "HzcInject"; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { (processingEnvironment); //赋值代码输出成⽂件的⼯具类 singEnvironment = processingEnvironment; } /** * 主要检索回调的⼊⼝,所以编译动作从这⾥开始 */ @Override public boolean process(Set set, RoundEnvironment roundEnvironment) { List javaFiles = new ArrayList<>(); List serviceList = new ArrayList<>(); //得到所有⽤到Service注解的元素 for (Element p : mentsAnnotatedWith()) { try { (ng()); * RoundEnvironment 包含了检索的结果,也就是说,哪些类⽤了上⾯SupportedAnnotationTypes中指定的注解类,以及相关可操作的值,反正⼀切和注解有关的都 (ng()); } catch (Exception e) { } } //havapoet⽣成java⽂件,不熟悉javapoet的同学⾃⼰查资料吧 TypeSpec hzcInject = uilder(CLASS_NAME) .superclass((, , )) .addModifiers(, ) .addMethod(Constructor()) .addMethod(eInject()) .addMethod(StaticInstance()) .addType(InnerClass()) .addMethod(InitService(serviceList)).build(); (r(PACKAGE_NAME, hzcInject).build()); Map> activitys = new HashMap<>(); for (Element p : mentsAnnotatedWith()) { String key = losingElement().toString(); List elementList = (key); if (elementList == null) elementList = new ArrayList<>(); (p); (key, elementList); } for (String key : ()) { List elementList = (key); (r(PACKAGE_NAME + ".model", InjectClass(key, elementList)).build()); } int size = (); for (int i = 0; i < size; i++) { try { (i).writeTo(er()); } catch (IOException e) { } } //告诉处理器,,处理完成了 return true; }}解析:创建⼀个类去实现ctProcessor,同时将这个类的完整包路径写⼊到sor⽂件中.

在类的头部告诉处理器需要处理检索的注解类,并制定编译版本,然后在process中对检索的结果进⾏处理.

mentsAnnotatedWith()//获得所有使⽤了这个注解的对象

ng //获得当前使⽤了注解的对象的完整包类名,如果作⽤在属性,那就是属性名,如果作⽤在类

那么就是类名

losingElement().toString()//当作⽤在属性的时候,获得这个属性所在的类全称

otationMirrors()//获得元素的所有注解

otationMirrors().get(0).getElementValues()//获得注解的值Map对象

pleName()//获得使⽤了注解的类/属性的名称

()//获得使⽤了注解的类/属性的类型到此,完成⼀个编译时注解基本流程,剩下的⼯作就是,把原来运⾏时注解的代码⽤javapoet进⾏翻译就可以了.