您的位置:首页 > 其它

ARouter 学习-源码分析如何暴露服务

2018-01-31 17:57 821 查看
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) {

a654
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是如何做到块模块调用到暴露的服务。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Arouter