我们都知道 ARouter 实现页面跳转很简单,在 Activity 上加上 @Router 注解,就能通过这个路径完成跳转。可如果去看调用方法的实现,并不能一眼看出实现原理。所以就有了这篇文章,要想了解整个实现环节,还得从注解角度去分析。
内容主要分为两部分,一部分是 Java 注解,借此机会大致了解些,但不会都介绍;另一部分是 APT 机制,这个是我新了解的,了解了这个,再去看很多第三方框架,你会发现透彻很多。
ARouter 里用到的 Java 注解
就以 Route 为例展开分析,
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
String path();
String group() default "";
String name() default "";
//省略
}
第一行的 @Target 表示 Route 注解是哪种 Java 成员。@Target 也是一个注解,以它为例,再展开看看,
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
从 Target 的方法 value() 来看可以指定多种类型,不过 Route 只赋值了一种,即 ElementType.TYPE,表示 类,接口或者枚举,这里先简单理解为类。ElementType 是个枚举类,里面还有其他值,例如 FIELD, METHOD 等等。
再说 Target 注解,@Decumented 表示 Target 包含在用户文档中,了解即可。@Retention 表示 Target 是怎么保存的,例如只在代码中不被编译器识别,还是仅编入 class 文件,或者运行时通过反射访问。这里值是 RetentionPolicy.RUNTIME 表示保存在会被编入 class 文件,并可以通过运行时反射访问,RetentionPolicy 也是一个枚举类。
最后 Target 注解也有一个 @Target,其值表示它的类型是注解声明。
回看 Route 注解,这里我们就知道了它的类型是 Java 类,并且仅编入 class 文件保存。(这里可以先记住,后面可以自己留意下为什么这么做)
关于 Java 注解可以看下这个更详细的介绍。Java 注解
ARouter 如何使用 APT
APT(注解处理器),可以将运行时用反射处理注解的过程提前到编译时期,从而提高效率。它的作用是,项目编译时拿到相应的注解和被注解对象,从而依照需求自动生成一些代码。
在项目引入 ARouter 框架时,需要像这样添加依赖,
//以 kotlin 为例
implementation 'com.alibaba:arouter-api:1.5.1'
kapt 'com.alibaba:arouter-compiler:1.5.1'
为什么别的依赖一行就够了,而 ARouter 需要两行,原因在于 ARouter 使用了 APT,一个 APT 项目要求,至少有两个 Java Library 模块。一个是 Annotation 模块用于存放自定义注解,一个 Compiler 模块,用于存放自定义注解处理器。
对照着 ARouter 在 github 上的项目工程,可以找到 arouter-annotation 和 arouter-compiler 这两个 Module。
想要实现对注解的处理,就要通过继承 Java 的 AbstractProcessor 抽象类来实现。前面为什么是 Java 库模块,因为 Android 库模块里没有 AbstractProcessor,这个类主要的方法有这么几个,
//表示处理器支持的注解
//也可以利用 @SupportedAnnotationTypes 以注解参数的形式添加
public Set<String> getSupportedAnnotationTypes()
//表示处理器支持的最新版本,默认是 1.6 版本
//也可以利用 @SupportedSourceVersion 以注解参数的形式指定
public SourceVersion getSupportedSourceVersion()
//初始化处理器,重点在于 ProcessingEnvironment 类型,这里面包含着很多信息
public synchronized void init(ProcessingEnvironment processingEnv)
//唯一一个抽象方法
//自定义处理器的工作也在此方法里
public abstract boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
参考 arouter-compiler Module 里的 processor 包,BaseProcessor 继承自 AbstractProcessor 应该是做一些公共操作,先不管,再看 RouteProcessor 继承自 BaseProcessor,这个应该就是用来做 Route 注解处理的处理器。
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv){}
public boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv){}
}
从第一行开始看,@AutoService 注解帮助我们自动生成一个文件,可以理解为我们自定义注解处理器后需要做一步这样的配置,而 @AutoService 就能帮我们完成这步,省去了人工操作。注意需要添加相应的依赖才能使用,AutoService
//arouter 源码里是这样引用的
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
compileOnly 'com.google.auto.service:auto-service-annotations:1.0-rc7'
第二行就是前面说的处理器支持的类型,显然它支持 Route 和 AutoWired,但这两个常量的值其实是带上包名的路径,"com.alibaba.android.arouter.facade.annotation.Route" 和 "com.alibaba.android.arouter.facade.annotation.Autowired" 根据这个路径,其实指到的也就是 Route 和 Autowired 注解。
再往下就是 RouteProcessor 的主要工作了,分两部分,一部分是初始化工作,一部分是 process 工作。最终的目标就是根据注解生成相应的类代码,但如果结构化编写很容易出错,鉴于面向对象的思想,可能会更好理解,所以就有了 JavaPoet 这个库,果然就是有需求就会有产出。
//arouter 源码里是这样引用的
implementation 'com.squareup:javapoet:1.8.0'
所以综合来看,ARouter 采用了 APT 机制来帮助框架自动生成一些代码,并且将生成时机提前至编译器,这样一来,效率就提高了。
网友评论