ARouter 使用了@Autowired , @Route等注解来实现需要的功能,我们的源码分析也从注解相关的模块开始。ARouter的源码中与注解相关的模块是 arouter-annotation (包含了所有注解类) , arouter-compiler (即APT模块)。
对APT(注解处理器)还不了解的可以先看这篇博客
这个模块的目录结构如下
参数自动注入的注解,对需要自动注入的成员变量标记,ARouter跳转时就可以将值注入
name
为要注入的参数值的自定义名称required
为是否进行非空检验(基本类型不检验),如果设置为true并再跳转时给自动绑定参数传入null的话会抛出异常desc
略/**
* Annotation for field, which need autowired.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/20 下午4:26
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
// Mark param's name or service name.
String name() default "";
// If required, app will be crash when value is null.
// Primitive type wont be check!
boolean required() default false;
// Description of the field
String desc() default "";
}
拦截器的注解,用来标记作为拦截器的类,这个类必须实现Interceptor
接口
priority
拦截器的优先级name
略/**
* Mark a interceptor to interception the route.
* BE ATTENTION : This annotation can be mark the implements of #{IInterceptor} ONLY!!!
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 14:03
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
/**
* The priority of interceptor, ARouter will be excute them follow the priority.
*/
int priority();
/**
* The name of interceptor, may be used to generate javadoc.
*/
String name() default "Default";
}
路由节点的注解,我们可以看到这里的官方描述里没有更新,@Route注解不仅可以标记Activity , Fragment还可以标记 Service (这里说的Service是实现了IProvider的类,并不是安卓的四大组件那个Service)
/**
* Mark a page can be route by router.
*
* @author Alex Contact me.
* @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 "";
/**
* Name of route, used to generate javadoc.
*/
String name() default "";
/**
* Extra data, can be set by user.
* Ps. U should use the integer num sign the switch, by bits. 10001010101010
*/
int extras() default Integer.MIN_VALUE;
/**
* The priority of route.
*/
int priority() default -1;
}
路由节点类型的枚举类,由于ARouter仍在维护中,之后可能会支持更多类型,但目前路由节点仅支持Activity , Provider , Fragment
/**
* Type of route enum.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 22:33
*/
public enum RouteType {
ACTIVITY(0, "android.app.Activity"),
SERVICE(1, "android.app.Service"),
PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
BOARDCAST(-1, ""),
METHOD(-1, ""),
FRAGMENT(-1, "android.app.Fragment"),
UNKNOWN(-1, "Unknown route type");
int id;
String className;
public int getId() {
return id;
}
public RouteType setId(int id) {
this.id = id;
return this;
}
public String getClassName() {
return className;
}
public RouteType setClassName(String className) {
this.className = className;
return this;
}
RouteType(int id, String className) {
this.id = id;
this.className = className;
}
public static RouteType parse(String name) {
for (RouteType routeType : RouteType.values()) {
if (routeType.getClassName().equals(name)) {
return routeType;
}
}
return UNKNOWN;
}
}
参数类型的枚举类,用于代表需要自动注入的参数的类型。
/**
* Kind of field type.
*
* @author Alex Contact me.
* @version 1.0
* @since 2017-03-16 19:13:38
*/
public enum TypeKind {
// Base type
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,
// Other type
STRING,
SERIALIZABLE,
PARCELABLE,
OBJECT;
}
这是一个比较重要的类,它包含了一次路由操作需要的所有信息,来看一看它的参数
RouteType type
目标路由节点的类型,用RouteType类(上文已提过)的实例来表示Element rawType
目标路由节点的Element , 对Element类还不了解的可以先面向搜索引擎学习一下Class> destination
目标路由节点的Class 对象String path
目标路由节点的path,(即@Route注解中的path值)String group;
目标路由节点的group,(即@Route注解中的group值)int priority
目标路由节点的priority,(即@Route注解中的priority值)int extra
目标路由节点的extra,(即@Route注解中的extra值)Map paramsType
目标路由节点中需要自动注入的参数的(名称-参数类型)映射关系,这里的Integer值代表着枚举类TypeKind(上文已提过)的 ordinal 值String name
略Map injectConfig
该路由节点中需要自动注入的(成员变量名-@Autowired)的映射关系代码虽然很长,但都是构造方法和getter,setter方法,这也符合这个类的描述(用来储存路由信息
/**
* It contains basic route information.
*
* @author Alex Contact me.
* @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
private String name;
private Map<String, Autowired> injectConfig; // Cache inject config.
public RouteMeta() {
}
/**
* For versions of 'compiler' less than 1.0.7, contain 1.0.7
*
* @param type type
* @param destination destination
* @param path path
* @param group group
* @param priority priority
* @param extra extra
* @return this
*/
public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, int priority, int extra) {
return new RouteMeta(type, null, destination, null, path, group, null, priority, extra);
}
/**
* For versions of 'compiler' greater than 1.0.7
*
* @param type type
* @param destination destination
* @param path path
* @param group group
* @param paramsType paramsType
* @param priority priority
* @param extra extra
* @return this
*/
public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
return new RouteMeta(type, null, destination, null, path, group, paramsType, priority, extra);
}
/**
* Type
*s0
* @param route route
* @param destination destination
* @param type type
*/
public RouteMeta(Route route, Class<?> destination, RouteType type) {
this(type, null, destination, route.name(), route.path(), route.group(), null, route.priority(), route.extras());
}
/**
* Type
*
* @param route route
* @param rawType rawType
* @param type type
* @param paramsType paramsType
*/
public RouteMeta(Route route, Element rawType, RouteType type, Map<String, Integer> paramsType) {
this(type, rawType, null, route.name(), route.path(), route.group(), paramsType, route.priority(), route.extras());
}
/**
* Type
*
* @param type type
* @param rawType rawType
* @param destination destination
* @param path path
* @param group group
* @param paramsType paramsType
* @param priority priority
* @param extra extra
*/
public RouteMeta(RouteType type, Element rawType, Class<?> destination, String name, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
this.type = type;
this.name = name;
this.destination = destination;
this.rawType = rawType;
this.path = path;
this.group = group;
this.paramsType = paramsType;
this.priority = priority;
this.extra = extra;
}
public Map<String, Integer> getParamsType() {
return paramsType;
}
public RouteMeta setParamsType(Map<String, Integer> paramsType) {
this.paramsType = paramsType;
return this;
}
public Map<String, Autowired> getInjectConfig() {
return injectConfig;
}
public void setInjectConfig(Map<String, Autowired> injectConfig) {
this.injectConfig = injectConfig;
}
public Element getRawType() {
return rawType;
}
public RouteMeta setRawType(Element rawType) {
this.rawType = rawType;
return this;
}
public RouteType getType() {
return type;
}
public RouteMeta setType(RouteType type) {
this.type = type;
return this;
}
public Class<?> getDestination() {
return destination;
}
public RouteMeta setDestination(Class<?> destination) {
this.destination = destination;
return this;
}
public String getPath() {
return path;
}
public RouteMeta setPath(String path) {
this.path = path;
return this;
}
public String getGroup() {
return group;
}
public RouteMeta setGroup(String group) {
this.group = group;
return this;
}
public int getPriority() {
return priority;
}
public RouteMeta setPriority(int priority) {
this.priority = priority;
return this;
}
public int getExtra() {
return extra;
}
public RouteMeta setExtra(int extra) {
this.extra = extra;
return this;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "RouteMeta{" +
"type=" + type +
", rawType=" + rawType +
", destination=" + destination +
", path='" + path + '\'' +
", group='" + group + '\'' +
", priority=" + priority +
", extra=" + extra +
", paramsType=" + paramsType +
", name='" + name + '\'' +
'}';
}
}
这个类是用来获得某个类的Type实例
这也是获得Type实例的常用的一种写法
对Type还不了解的可以先面向搜索引擎学习一下
/**
* Used for get type of target object.
*
* @author Alex Contact me.
* @version 1.0
* @since 17/10/26 11:56:22
*/
public class TypeWrapper<T> {
protected final Type type;
protected TypeWrapper() {
Type superClass = getClass().getGenericSuperclass();
type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public Type getType() {
return type;
}
}
arouter-annation这个模块比较简单,都是一些基本的类,不涉及什么逻辑,但这些类在核心模块中起到重要作用
从命名中就可以猜到这个模块就是APT模块
这个模块的目录如下
这个类主要是用于生成路由表供查看,不涉及框架逻辑,这里不做解析
/**
* Description route info, used for generate router map
*
* @author zhilong Contact me.
* @version 1.0
* @since 2018/8/9 11:59 AM
*/
public class RouteDoc {
...
}
APT的处理类,用于处理@Autowired
这个注解
对APT(注解处理器)还不了解的可以先看这篇博客
这个类的代码很长,我们逐段来看
首先开头四个注解,以及init()方法都是APT的基本写法,这里不做赘述
来看一个重要的成员变量
Map> parentAndChild
储存着 (路由节点 - 需要自动注入的参数)的映射关系/**
* Processor used to create autowired helper
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/20 下午5:56
*/
@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends AbstractProcessor {
private Filer mFiler; // File util, write class file into disk.
private Logger logger;
private Types types;
private TypeUtils typeUtils;
private Elements elements;
private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>(); // Contain field need autowired and his super class.
private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
private static final ClassName AndroidLog = ClassName.get("android.util", "Log");
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
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.
logger.info(">>> AutowiredProcessor init. <<<");
}
...
}
接着来看核心的process方法,我们可以看到这里分别调用了categories
方法并通过roundEnvironment.getElementsAnnotatedWith(Autowired.class)
获得所有被@Autowired
标记的Element实例并作为参数传入,然后再调用generateHelper生成帮助类文件
public class AutowiredProcessor extends AbstractProcessor {
...
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
logger.info(">>> Found autowired field, start... <<<");
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
}
我们先来看categories
方法, 这个方法就是对所有传进来的element进行扫描,将它们所在的路由节点和它们的映射关系存到上文提到的parentAndChild参数中
/**
* Categories field, find his papa.
*
* @param elements Field need autowired
*/
private void categories(Set<? extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
for (Element element : elements) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); //获取所在类(即路由节点)的TypeElement实例
if (element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
} //被标记的成员变量不能为private,否则抛出异常
if (parentAndChild.containsKey(enclosingElement)) {
parentAndChild.get(enclosingElement).add(element);
} else {
List<Element> childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
} //将映射关系添加到parentAndChild中
}
logger.info("categories finished.");
}
}
这里补充说明一下,因为这个框架不是直接依赖反射去实现成员变量的自动注入的,所以成员变量的修饰符不能为private
然后我们再来看generateHelper()这个方法,这个方法用来生成帮助类文件,使用了javapoet这个开源库来生成java文件 , 不了解的可以先移步官方文档。 这个方法的代码比较长,我在关键部分都添加了注释
private void generateHelper() throws IOException, IllegalAccessException {
TypeElement type_ISyringe = elements.getTypeElement(ISYRINGE);
TypeElement type_JsonService = elements.getTypeElement(JSON_SERVICE);
TypeMirror iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
TypeMirror activityTm = elements.getTypeElement(Consts.ACTIVITY).asType();
TypeMirror fragmentTm = elements.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();
// JavaPoet语法 构建帮助类的inject方法的形参Object target
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
if (MapUtils.isNotEmpty(parentAndChild)) {
//遍历parentAndChild,每一次循环生成一个帮助类文件,即为每一个含有自动注入变量的路由节点都生成一个帮助类
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
// JavaPoet语法,构建public inject(Object target)方法
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
TypeElement parent = entry.getKey();
List<Element> childs = entry.getValue();
String qualifiedName = parent.getQualifiedName().toString();//获得路由节点类的全限定名
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));//获得包名,帮助类的包名和路由节点类的包名一致
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED; // NAME_OF_AUTOWIRED = "$$Arouter$$Autowired"
logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");
//JavaPoet 语法,构建帮助类,修饰符为public,并实现ISyringe接口
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
//JavaPoet 语法,构建成员变量private SerializationService serializationService
//这个成员变量是用于序列化自定义的对象
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
helper.addField(jsonServiceField);
//构建inject方法体serializationService = ARouter.getInstance().navigation(SerializationService.class);
//可以看到实例的获得是通过Arouter获得服务的方式
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
// 构建inject方法体,遍历childs,为每一个childs添加赋值语句
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);//获得注解
String fieldName = element.getSimpleName().toString();//获得成员变量名
//如果这个成员变量是IPrivider 的实现类(即自定义的各种服务),就通过ARouter获取服务的方式去获得实例
//如果在@Autowired这个注解中指定了name,则通过byName方式,否则通过byType方式
if (types.isSubtype(element.asType(), iProvider)) {
if ("".equals(fieldConfig.name())) { // User has not set service path, then use byType.
// byType方式
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else {
// byName方式
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// 如果需要进行非空检验,则添加非空检验的语法
if (fieldConfig.required()) {
injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
injectMethodBuilder.addStatement(
"throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
// 这个成员变量不是IProvider
// 通过isActivity标记目标路由节点是不是Activty
else {
String originalValue = "substitute." + fieldName;
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(), activityTm)) { // Activity, then use getIntent()
isActivity = true;
statement += "getIntent()."; //是Activity就通过getIntent()的方式获得传递过来的参数
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { // Fragment, then use getArguments()
statement += "getArguments()."; // 是fragment就通过getArguments()的方式获得传递过来的参数
} else {
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
//JavaPoet语法
statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity);
if (statement.startsWith("serializationService.")) { // Not mortals
injectMethodBuilder.beginControlFlow("if (null != serializationService)");
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = " + statement,
(StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
ClassName.get(element.asType())
);
injectMethodBuilder.nextControlFlow("else");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
} else {
injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}
// 如果需要进行非空检验,则添加非空检验的语法
if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) { // Primitive wont be check.
injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
}
//JavaPoet语法
helper.addMethod(injectMethodBuilder.build());
//输出帮助类的java文件
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
}
logger.info(">>> Autowired processor stop. <<<");
}
}
AutowiredProcessor用来生成各种以 A r o u t e r Arouter ArouterAutowired为后缀的帮助类文件
这些帮助类最后会在运行时被用来进行成员变量的自动注入
使用帮助类而不是运行时直接反射对成员变量赋值,避免了反射过多的开销
和上面一样的步骤,我们逐段来分析
有一个重要成员变量 :
Map interceptors
存放着优先级和拦截器的映射关系值得一提的是,interceptors的初始化是new了一个TreeMap的实例,我们都知道TreeMap默认会按照key进行排序,所以这就实现了拦截器按优先级进行排序
init() 方法里面除了获得mFilter
, elmentUtil
, logger
几个工具类实例之外,主要逻辑就是获得模块名,以及获得IInterceptor
接口的TypeElement
实例并赋值给iInterceptor
/**
* Process the annotation of #{@link Interceptor}
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 14:11
*/
@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes(ANNOTATION_TYPE_INTECEPTOR)
public class InterceptorProcessor extends AbstractProcessor {
private Map<Integer, Element> interceptors = new TreeMap<>();
private Filer mFiler; // File util, write class file into disk.
private Logger logger;
private Elements elementUtil;
private String moduleName = null; // Module name, maybe its 'app' or others
private TypeMirror iInterceptor = null;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler(); // Generate class.
elementUtil = processingEnv.getElementUtils(); // Get class meta.
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.");
}
iInterceptor = elementUtil.getTypeElement(Consts.IINTERCEPTOR).asType();
logger.info(">>> InterceptorProcessor init. <<<");
}
...
}
接着来看process()方法,里面主要就是调用了parseInterceptors
方法
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);
try {
parseInterceptors(elements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
跟着来到parseInterceptors
方法
private void parseInterceptors(Set<? extends Element> elements) throws IOException {
if (CollectionUtils.isNotEmpty(elements)) {
logger.info(">>> Found interceptors, size is " + elements.size() + " <<<");
for (Element element : elements) {
if (verify(element)) { //verify方法见后文
logger.info("A interceptor verify over, its " + element.asType());
Interceptor interceptor = element.getAnnotation(Interceptor.class);
Element lastInterceptor = interceptors.get(interceptor.priority());
if (null != lastInterceptor) { //如果已添加过相同优先级的拦截器,则抛出异常
throw new IllegalArgumentException(
String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",
interceptor.priority(),
lastInterceptor.getSimpleName(),
element.getSimpleName())
);
}
//将拦截器的优先级和拦截器的映射关系添加进去
interceptors.put(interceptor.priority(), element);
} else {
logger.error("A interceptor verify failed, its " + element.asType());
}
}
// 分别为IInterceptor和IInterceptorGroup接口的TypeElement实例
TypeElement type_ITollgate = elementUtil.getTypeElement(IINTERCEPTOR);
TypeElement type_ITollgateGroup = elementUtil.getTypeElement(IINTERCEPTOR_GROUP);
//JavaPoet语法,构建形参类型Map>
ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(Integer.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_ITollgate))
)
);
//JavaPoet语法,构建形参名
ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();
// JavaPoet语法,构建loadinto方法
MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(tollgateParamSpec);
// 构建方法体,为每一个interceptor都添加一条类似interceptors.put(7, Test1Interceptor.class)的语句
if (null != interceptors && interceptors.size() > 0) {
// Build method body
for (Map.Entry<Integer, Element> entry : interceptors.entrySet()) {
loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));
}
}
// 生成Arouter$$Interceptors$$(模块名) 文件
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName)
.addModifiers(PUBLIC)
.addJavadoc(WARNING_TIPS)
.addMethod(loadIntoMethodOfTollgateBuilder.build())
.addSuperinterface(ClassName.get(type_ITollgateGroup))
.build()
).build().writeTo(mFiler);
logger.info(">>> Interceptor group write over. <<<");
}
}
verify
方法很简单,就是判断传入的element有没有@Interceptor注解并实现了IIntercptor接口
/**
* Verify inteceptor meta
*
* @param element Interceptor taw type
* @return verify result
*/
private boolean verify(Element element) {
Interceptor interceptor = element.getAnnotation(Interceptor.class);
// It must be implement the interface IInterceptor and marked with annotation Interceptor.
return null != interceptor && ((TypeElement) element).getInterfaces().contains(iInterceptor);
}
这是最重要的注解@Route 的处理器,所以它的代码也是最长的。。。
逐段来看, 有两个重要的成员变量
init
方法,和InterceptorProcessor
几乎一模一样,都是获取模块名。多了个打印映射表的逻辑
@AutoService(Processor.class)
@SupportedOptions({KEY_MODULE_NAME, KEY_GENERATE_DOC_NAME})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends AbstractProcessor {
private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta.
private Map<String, String> rootMap = new TreeMap<>(); // Map of root metas, used for generate class file in order.
private Filer mFiler; // File util, write class file into disk.
private Logger logger;
private Types types;
private Elements elements;
private TypeUtils typeUtils;
private String moduleName = null; // 模块名
private TypeMirror iProvider = null;
private boolean generateDoc; // If need generate router doc
private Writer docWriter; // Writer used for write doc
/**
* Initializes the processor with the processing environment by
* setting the {@code processingEnv} field to the value of the
* {@code processingEnv} argument. An {@code
* IllegalStateException} will be thrown if this method is called
* more than once on the same object.
*
* @param processingEnv environment to access facilities the tool framework
* provides to the processor
* @throws IllegalStateException if this method is called more than once.
*/
@Override
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);
generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_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" +
"android {\n" +
" defaultConfig {\n" +
" ...\n" +
" javaCompileOptions {\n" +
" annotationProcessorOptions {\n" +
" arguments = [AROUTER_MODULE_NAME: project.getName()]\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n");
throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
}
//打印映射表,略
if (generateDoc) {
try {
docWriter = mFiler.createResource(
StandardLocation.SOURCE_OUTPUT,
PACKAGE_OF_GENERATE_DOCS,
"arouter-map-of-" + moduleName + ".json"
).openWriter();
} catch (IOException e) {
logger.error("Create doc writer failed, because " + e.getMessage());
}
}
iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
logger.info(">>> RouteProcessor init. <<<");
}
按套路,接着来看process
方法,可以看到里面又调用了parseRoutes
方法
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;
}
由于parseRoutes
方法过长,我们拆分来看,首先还是获取各种TypeElement , TypeMirror用来标识类型
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
// prepare the type an so on.
logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
rootMap.clear();
TypeMirror type_Activity = elements.getTypeElement(ACTIVITY).asType();
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);
...
}
注:这里的Service是四大组件中的Service, 从这段源码中我们可以推测出ARouter将来可能会支持向服务的路由
紧接着就是一连串的JavaPoet的语法,用来各种帮助类的构建loadinto
方法
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
...
/*
构建ARouter$$Root$$(包名)类的loadinto方法的形参类型
```Map>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
构建ARouter$$Group$$(分组名)类 和 ARouter$$Providers$$(模块名)类
的loadinto方法的形参类型
```Map```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*
Build input param name.
*/
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
/*
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
...
}
接着来看,遍历routeElements
, 而routeElements
中存的是含有@Route
注解的Element
,所以这里每一次循环实际上是对一个含有@Route
的(Activity
或 IProvider
或 Service
或 Fragment
)分类进行处理,最后调用了categories
方法
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
...
// 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 routeMeta;
//如果这个节点是一个Activity
if (types.isSubtype(tm, type_Activity)) { // Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
// 对这个Activity中含有@Autowired注解的成员变量进行处理
// 只要这个成员变量不是iProvider , 就将它的信息放入routeMeta.injectConfig
// 因为从上文我们知道自动注入时iProvider和普通的成员变量赋值的方式并不一样,所以这里这样做也很好理解
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = 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);
String injectName = StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name();
paramsType.put(injectName, typeUtils.typeExchange(field));
injectConfig.put(injectName, paramConfig);
}
}
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) { // IProvider
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) { // Service
logger.info(">>> Found service route: " + tm.toString() + " <<<");
routeMeta = 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() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null);
} else {
throw new RuntimeException("ARouter::Compiler >>> Found unsupported class type, type = [" + types.toString() + "].");
}
categories(routeMeta);
}
...
}
这段代码执行完之后,就会得到一个routeMeta
,然后再将这个routeMeta
传入到categories
中
然后我们接着来看categories
方法,这个方法很简单,就是对传进来的routeMete
根据所属分组的不同存到groupMap
中
private void categories(RouteMeta routeMete) {
if (routeVerify(routeMete)) { //检验路径合法性
logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());
if (CollectionUtils.isEmpty(routeMetas)) {
Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {
@Override
public int compare(RouteMeta r1, RouteMeta r2) {
try {
return r1.getPath().compareTo(r2.getPath());
} catch (NullPointerException npe) {
logger.error(npe.getMessage());
return 0;
}
}
});
routeMetaSet.add(routeMete);
groupMap.put(routeMete.getGroup(), routeMetaSet);
} else {
routeMetas.add(routeMete);
}
} else {
logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
}
}
我们再回到parseRoute
方法, 关键部分添加了注解,这里有一个小彩蛋,读者仔细观察里面有一句开发者的吐槽注释"So stupid, will duplicate only save class name"
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
...
//JavaPoet语法,构建Provider的loadInto方法
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
//打印映射表相关,略
Map<String, List<RouteDoc>> docSource = new HashMap<>();
// 开始生成java文件
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
// JavaPoet语法,构建分组的loadInto方法
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
List<RouteDoc> routeDocList = new ArrayList<>();
// 构建方法里的内容
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
//
RouteDoc routeDoc = extractDocInfo(routeMeta);
//类名
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
//上文提到Provider的映射关系在ARouter$$Group$***和ARouter$$Providers$$***
//中都会出现,这段代码块就是构建ARouter$$Providers$$***文件中的loadInto方法
switch (routeMeta.getType()) {
case PROVIDER: // 有直接实现IProvider和实现IProvider子接口两种情况
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
routeDoc.addPrototype(tm.toString());
if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
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,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
}
// 在paramsType 中存入需要自动绑定的成员变量的信息
StringBuilder mapBodyBuilder = new StringBuilder();
Map<String, Integer> paramsType = routeMeta.getParamsType();
Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();
if (MapUtils.isNotEmpty(paramsType)) {
List<RouteDoc.Param> paramList = new ArrayList<>();
for (Map.Entry<String, Integer> types : paramsType.entrySet()) {
mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");
RouteDoc.Param param = new RouteDoc.Param();
Autowired injectConfig = injectConfigs.get(types.getKey());
param.setKey(types.getKey());
param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());
param.setDescription(injectConfig.desc());
param.setRequired(injectConfig.required());
paramList.add(param);
}
routeDoc.setParams(paramList);
}
String mapBody = mapBodyBuilder.toString();
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
routeDoc.setClassName(className.toString());
routeDocList.add(routeDoc);
}
// 生成ARouter$$Group$$***文件
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);
logger.info(">>> Generated group: " + groupName + "<<<");
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
//构建ARouter$$
if (MapUtils.isNotEmpty(rootMap)) {
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
// 打印映射表相关,略
if (generateDoc) {
docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
docWriter.flush();
docWriter.close();
}
// 生成ARouter$$PRoviders$$*** 文件
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 + " <<<");
// 生成ARouter$$Root$$*** 文件
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);
logger.info(">>> Generated root, name is " + rootFileName + " <<<");
}
}