Arouter提供自定义注解Route方便应用去标注哪些服务可以暴露给其它模块。
**
* Mark a page can be route by router.
*
* @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 16/8/15 下午9:29
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
/**
* Path of route
*/
String path();
/**
* Used to merger routes, the group name MUST BE USE THE COMMON WORDS !!!
*/
String group() default "";
标注了就可以暴露?是的,不过是依靠自定义注解处理器来实现的,下面开始分析arouter-compiler中专门用来处理Route的处理器 RouteProcessor。
一个自定义注解处理器大致的代码结构如下:
public class ProcessorA extends AbstractProcessor {
//这个方法主要是获取工具类
public synchronized void init(ProcessingEnvironment env){ }
//这个方法里面写处理的过程,输入参数annotations是getSupportedAnnotationTypes()的子集,即是工程代码中所有注解集合与getSupportedAnnotationTypes()的交集,RoundEnvironment env代表这一轮扫描后的结果,返回true则表示消费完此次扫描,此轮扫描注解结束
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { }
//返回需要处理的注解的类的getCanonicalName()集合
public Set<String> getSupportedAnnotationTypes() { }
//返回SourceVersion.latestSupported()即可
public SourceVersion getSupportedSourceVersion() { }
}
重点需要关注init和process方法。
RouterProcessor init方法
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler(); // Generate class.
types = processingEnv.getTypeUtils(); // Get type utils.
elements = processingEnv.getElementUtils(); // Get class meta.
typeUtils = new TypeUtils(types, elements);
logger = new Logger(processingEnv.getMessager()); // Package the log utils.
// Attempt to get user configuration [moduleName]
Map<String, String> options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
}
if (StringUtils.isNotEmpty(moduleName)) {
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
logger.info("The user has configuration the module name, it was [" + moduleName + "]");
} else {
logger.error("These no module name, at 'build.gradle', like :\n" +
"apt {\n" +
" arguments {\n" +
" moduleName project.getName();\n" +
" }\n" +
"}\n");
throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
}
iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
logger.info(">>> RouteProcessor init. <<<");
}
人如起名,init做的就是初始化操作,作者也备注了重要的几个参数,就不说了。进入process方法。
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routes, start... <<<");
this.parseRoutes(routeElements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class)
获取到所有被Route注解过得Element. 然后调用parseRoutes方法解析Route.
parseRoutes方法较长,分段来分析。
第一段
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
// Perpare the type an so on.
logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
rootMap.clear();
TypeMirror type_Activity = elements.getTypeElement(ACTIVITY).asType();
logger.info(">>>print type_activity:"+type_Activity);
TypeMirror type_Service = elements.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();
// Interface of ARouter
TypeElement type_IRouteGroup = elements.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elements.getTypeElement(IPROVIDER_GROUP);
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
Element->表示一个程序元素,比如包,类或者方法。
TypeElement-> 表示一个类或者接口程序元素
TypeMirror->表示Java编程语言中的类型
举个例子:后面编织代码时会判断当前被注解的类是不是Activity,此时会用到type_Activity来做类型判断:
types.isSubtype(tm , type_Activity)
第一段主要是创建后面编织代码时会用到的TypeElement和TypeMirror.
那ClassName是什么???这里需要了解JavaPoet, javaPoet是用来帮助我们生成Java源码的工具。
JavaPoet几个常用的类
MethodSpec 代表一个构造函数或方法声明。
TypeSpec 代表一个类,接口,或者枚举声明
FieldSpec 代表一个成员变量,一个字段声明。
JavaFile包含一个顶级类的Java文件。
ParameterSpec 用来创建参数
AnnotationSpec 用来创建注解
TypeName 类型,如在添加返回值类型是使用 TypeName.VOID
ClassName 用来包装一个类
javapoet 常用的API
addStatement() 方法负责分号和换行
beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。
addCode() 以字符串的形式添加内
returns 添加返回值类型
.constructorBuilder() 生成构造器函数
.addAnnotation 添加注解
addSuperinterface 给类添加实现的接口
superclass 给类添加继承的父类
ClassName.bestGuess(“类全名称”) 返回ClassName对象,这里的类全名称表示的类必须要存在,会自动导入相应的包
ClassName.get(“包名”,”类名”) 返回ClassName对象,不检查该类是否存在
TypeSpec.interfaceBuilder(“HelloWorld”)生成一个HelloWorld接口
MethodSpec.constructorBuilder() 构造器
addTypeVariable(TypeVariableName.get(“T”, typeClassName))
具体如何使用不展开,结合Arouter是如何编制代码来分析。
第二段
/*
Build input type, format as :
```Map<String, Class<? extends IRouteGroup>>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
```Map<String, RouteMeta>```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*
Build input param name.
*/
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
/*
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
上面代码主要是利用JavaPoet创建参数和loadInfo方法,生成代码如下:
public void loadInto(Map<String , Class<? extends IRouteGroup>> routes) {
}
第三段
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
for (Element element : routeElements) {
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMete = null;
if (types.isSubtype(tm, type_Activity)) { // Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
// Get all fields annotation by @Autowired
Map<String, Integer> paramsType = new HashMap<>();
for (Element field : element.getEnclosedElements()) {
if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) {
// It must be field, then it has annotation, but it not be provider.
Autowired paramConfig = field.getAnnotation(Autowired.class);
paramsType.put(StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name(), typeUtils.typeExchange(field));
}
}
routeMete = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else if (types.isSubtype(tm, iProvider)) { // IProvider
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) { // Service
logger.info(">>> Found service route: " + tm.toString() + " <<<");
routeMete = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else if (types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
routeMete = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null);
} else {
throw new RuntimeException("ARouter::Compiler >>> Found unsupported class type, type = [" + types.toString() + "].");
}
categories(routeMete);
// if (StringUtils.isEmpty(moduleName)) { // Hasn't generate the module name.
// moduleName = ModuleUtils.generateModuleName(element, logger);
// }
}
因我们的目的主要是分析跨模块API调用,这里只需要关注
if ( types.isSubtype(tm , iProvider)) { // IProvider
logger.info( ">>> Found provider route: " + tm.toString() + " <<<") ;
routeMete = new RouteMeta(route , element , RouteType. PROVIDER , null) ;
}
发现是IProvider子类型后,将Route和element相关信息保存到RouteMeta结构体中。
/**
* It contains basic route information.
*
* @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 16/8/24 09:45
*/
public class RouteMeta {
private RouteType type; // Type of route
private Element rawType; // Raw type of route
private Class<?> destination; // Destination
private String path; // Path of route
private String group; // Group of route
private int priority = -1; // The smaller the number, the higher the priority
private int extra; // Extra data
private Map<String, Integer> paramsType; // Param type
public RouteMeta() {
}
routeMete信息会在categorites里排序后,按照组信息被加到:
private Map<String , Set<RouteMeta>> groupMap = new HashMap<>() ; // ModuleName and routeMeta.
第四段
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
// Build group method body
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
switch (routeMeta.getType()) {
case PROVIDER: // Need cache provider's super class
logger.info("print rawtype:"+routeMeta.getRawType());
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
logger.info(">>>sanme type");
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
ClassName.get((TypeElement) routeMeta.getRawType()),
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
Start generate java source.
4.1 第一部分生成方法:
public void loadInto(Map<String , RouteMeta> providers) {}
4.2 第二部分遍历groupMap:
先生成方法:
public void loadInto(Map<String , RouteMeta> atlas) {
}
然后遍历单个组内的RouteMeta信息:
for (RouteMeta routeMeta : groupData) {
switch (routeMeta.getType()) {
case PROVIDER:
// handle provider case.
}
}
重点关注如何处理Provider case.
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces() ;
这里是拿到被Route注解的类实现了哪些接口 。
for (TypeMirror tm : interfaces) {
if ( types.isSameType(tm , iProvider)) {
..................
}else if(types.isSubtype(tm,iProvider)){
..................
}
}
遍历interfaces,判断interface是自己实现了IProvider还是interface继承了IProvider.为什么要加两个判断?没有看太明白.
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
ClassName.get((TypeElement) routeMeta.getRawType()),
routeMeta.getPath(),
routeMeta.getGroup());
在ARouter\$\$Providers\$\$app类loadInfo方法中添加:
providers.put( "com.router.service.IService" , RouteMeta. build(RouteType. PROVIDER , MyService. class, "/service/hello" , "service" , null, - 1 , - 2147483648)) ;
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
ClassName.get((TypeElement) routeMeta.getRawType()),
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
在ARouter\$\$Group\$\$service类loadInfo方法中添加:
atlas.put( "/service/hello" , RouteMeta. build(RouteType. PROVIDER , MyService. class, "/service/hello" , "service" , null, - 1 , - 2147483648)) ;
4.3 第三部分生成Group java source file
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
将4.1和4.2生成的方法,写入以groupFileName命名的文件中。最终生成的类如下:
package com.alibaba.android.arouter.routes;
import com.alibaba.android.arouter.facade.enums.RouteType;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import com.router.service.MyService;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter\$\$Group\$\$service implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, MyService.class, "/service/hello", "service", null, -1, -2147483648));
}
}
第五段
// Wirte provider into disk
String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(providerMapFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IProviderGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfProviderBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated provider map, name is " + providerMapFileName + " <<<");
// Write root meta into disk.
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elements.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
最后生成两个类:
package com.alibaba.android.arouter.routes;
import com.alibaba.android.arouter.facade.enums.RouteType;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.template.IProviderGroup;
import com.router.service.MyService;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter\$\$Providers\$\$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.router.service.IService", RouteMeta.build(RouteType.PROVIDER, MyService.class, "/service/hello", "service", null, -1, -2147483648));
}
}
和
package com.alibaba.android.arouter.routes;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import com.alibaba.android.arouter.facade.template.IRouteRoot;
import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter\$\$Root\$\$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("service", ARouter$$Group$$service.class);
}
}
注解处理器处理Route分析完毕。
一个Route注解,经过自定义RouteProcessor的处理,最终生成三个类。那这些类有什么用呢???接下来分析arouter-api是如何做到块模块调用到暴露的服务。