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

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是如何做到块模块调用到暴露的服务。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值