介绍
在组件化开发的时候,组件之间是相互独立的没有依赖关系,我们不能在使用显示调用来跳转页面了,因为我们组件化的目的之一就是解决模块间的强依赖问题,假如现在要从A业务组件跳转到业务B组件,并且要携带参数跳转,这时候怎么办呢?
上学的时候在书上看到了一句很有意义的话:任何软件工程遇到的问题都可以通过增加一个中间层来解决!
我们从这句话出发去思考:组件之间是平行结构的,它们之间相互没有交集,要实现通信只有添加一个中间层将它们连接到一起,这就是“路由”的概念,由第一篇文章开始的组件化模型下的业务关系图可知路由就是起到一个转发的作用。路由就像一个桥梁一样让平行的河流(组件)之间可以通信和传递数据,这样看起来好像他们之间又是强耦合的关系,维护起来还是代价还有点大,那么还有没有好点的办法呢?那就是路由层使用接口和其他module之间建立弱耦合的关系。
在组件化开发中实现跨组件跳转的库比较有名气的是阿里的ARouter,ARouter的功能很丰富,这一节我们根据ARouter的源码理解自己实现一个简单版的ARouter,ARouter的功能太多了如过滤器,拦截器等,这里只是手写实现一部分核心功能-路由跳转。
准备
实现
定义注解
在router_annotation的module中定义注解Route.java
/**
* Target用于指定被此元注解标注的注解可以标出的程序元素
*
*/
@Target(ElementType.TYPE)
/**
* RetentionPolicy.SOURCE 源码阶段 注解信息只会保留在源码中,编译器在编译源码的时候会将其直接丢弃
* RetentionPolicy.CLASS 编译阶段 javapoet使用 注解信息保留在class文件中,VM不会持有其信息
* RetentionPolicy.RUNTIME 运行阶段,注解信息保留在class文件中,而且VM也会持有此注解信息, 反射获得注解信息
*/
@Retention(RetentionPolicy.CLASS)
public @interface Route {
/**
* 路由的路径,标识一个路由节点
*/
String path();
/**
* 将路由节点进行分组,可以实现按组动态加载
*/
String group() default "";
}
Route注解里有path和group,这是仿照ARouter对路由进行分组。因为当项目变得越来越庞大的时候,为了便于管理和减小首次加载路由表过于耗时的问题,我们对所有的路由进行分组。
定义注解处理器生成路由映射文件
定义RouteProcessor实现AbstractProcessor类, 这里使用google的 AutoService注册处理器,使用JavaPoet实现源文件的编写。在router_compiler的build.gradle引入这两个库
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
implementation project(':router_annotation')
}
// java控制台输出中文乱码
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
sourceCompatibility = "7"
targetCompatibility = "7"
Processor 一般会重写父类的4个方法:
init:初始化工作,我们可以得到一些有用的工具,例如 Filer,ElementUtils,TypeUtils.
process:最重要的方法,所有的注解处理都是在此完成
getSupportedAnnotationTypes:返回我们所要处理的注解的一个集合
getSupportedSourceVersion:要支持的java版本
@AutoService(Processor.class)
//处理器接受到的参数 代替 {@link AbstractProcessor#getSupportedOptions()} 函数
@SupportedOptions(Constants.ARGUMENTS_NAME)
/**
* 指定使用的Java版本 替代 {@link AbstractProcessor#getSupportedSourceVersion()} 函数
* 声明我们注解支持的JDK的版本
*/
@SupportedSourceVersion(SourceVersion.RELEASE_7)
/**
* 注册给哪些注解的 替代 {@link AbstractProcessor#getSupportedAnnotationTypes()} 函数
* 声明我们要处理哪一些注解 该方法返回字符串的集合表示该处理器用于处理哪些注解
*/
@SupportedAnnotationTypes(Constants.ANN_TYPE_ROUTE)
public class RouteProcessor extends AbstractProcessor {
....
/**
* key是组名,value是注解标记的element的元素数据集合
* 使用Route的注解按照组名分表存储
*/
private Map> groupMap = new HashMap<>();
/**
* key:组名 value:实现IRouteGroup接口的className
*/
private Map rootMap = new TreeMap<>();
...
}
init方法中根据ProcessingEnvironment得到一些工具和参数
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
Messager ms = processingEnvironment.getMessager();
Log.init(ms);
elementUtils = processingEnvironment.getElementUtils();
filerUtils = processingEnvironment.getFiler();
typeUtils = processingEnvironment.getTypeUtils();
Map options = processingEnvironment.getOptions();
if (!Utils.isEmpty(options)) {
moduleName = options.get(Constants.ARGUMENTS_NAME);
}
if (Utils.isBlank(moduleName)) {
throw new RuntimeException("Not set Processor Parmaters.");
}
}
扫描所有Route修饰的注解文件,根据组名进行分组
/**
* 处理注解
*
* @param set 使用了支持注解的节点集合
* @param roundEnvironment 上下文
* @return true 表示后续处理器不会再处理
*/
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {