阿里路由框架--ARouter 源码解析之Compiler

本文详细解析了阿里开源的ARouter路由框架的Compiler SDK部分,包括RouteProcessor、InterceptorProcessor和AutowireProcessor三大注解处理器的功能和处理流程。重点介绍了它们在编译期间如何根据注解生成相应的映射文件,并分析了各类生成文件的作用和目的。
摘要由CSDN通过智能技术生成

转载请注明出处:http://blog.csdn.net/crazy1235/article/details/77126904


前段时间,公司项目在做组件化重构,过程中当然会有很多痛点。

组件化最重要的是根据项目和业务进行分模块,至于模块的粒度就看大家自己来把控了!

这里要说的就是模块之间的数据传输问题

组件化之后,各个模块不相互依赖,那么怎么相互跳转和传递数据呢?

答案就是通过隐式Intent 的方式来跳转和传递数据。

以往的显示Intent 跳转,会存在类直接依赖的问题,这样会导致耦合性非常严重;相比而言,隐式Intent则不需要类之间的直接依赖,但是会出现规则集中式管理,扩展性比较差。

所以在调研期间就发现阿里开源了ARouter–路由框架。

ARouter的好处我这里就不多说,大家可以去看官方文档或者去github上看README。

https://github.com/alibaba/ARouter


接下来会分为若干篇blog来分析一下ARouter的源码!

看了ARouter的源码就会发现,它提供了两个SDK,一个是API,一个Compiler。

  • Compiler SDK 是用于编译器生成相关类文件的。

  • API SDK 是用在运行期间路由跳转等作用的。


这里写图片描述

这里先说说Compiler层SDK。

  • RouteProcessor 路由路径处理器

  • InterceptorProcessor 拦截器处理器

  • AutowireProcessor 自动装配处理器


注解处理器的处理流程

这里写图片描述

(图片转自网络)

实际上,Compiler SDK 只是处根据扫描到的注解生成相应的映射(java)文件。

最后一步通过固定包名加载映射文件是由API SDK来做的。

以官方demo为例来说:

这里写图片描述

上图所示就是ARouter在编译期间生成的类文件。

  • 红色标注的是 RouteProcessor 生成的类文件

  • 蓝色标注的是 InterceptorProcessor 生成的类文件

  • 橙色标书的是 AutowiredProcessor 生成的类文件

arouter-compiler的目录结构如下:

这里写图片描述

  • processor包下面是注解处理器

  • utils包下面是相关工具类

下面分别说说这三种注解处理器:


用过编译时注解的朋友们都知道,注解处理器需要继承AbstractProcessor ,主要涉及的函数有 init()process() 这两个。

RouteProcessor

类的继承信息:

@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends AbstractProcessor {
   

init

init()

    // 初始化处理器
    @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.

        // 获取用户配置的[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 {
            // 如果没有在build.gradle中配置moduleName,则会抛出异常。
            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();
        // RouterProcessor 初始化完毕
        logger.info(">>> RouteProcessor init. <<<");
    }
// Consts.java
public static final String KEY_MODULE_NAME = "moduleName";

在使用ARouter注解的时候,按照官方文档是需要在每个module里面的build.gradle中配置如下信息:

javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ moduleName : project.getName() ]
            }
        }

配置这个属性的目的,就是为了在编译期间生成相关module下的文件和存储文件名称。


process()

一般在process()函数中做的操作如下:

  • 遍历注解的元素

  • 检验元素是否符合要求(过滤元素)

  • 获取输出类参数

  • 生成映射文件(java文件)

  • 错误处理

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (CollectionUtils.isNotEmpty(annotations)) {
            // 获取所有添加Route注解的元素
            Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
            try {
                logger.info(">>> Found routes, start... <<<");
                // 调用arseRoute()函数进行处理获取的注解元素集合
                this.parseRoutes(routeElements);

            } catch (Exception e) {
                logger.error(e);
            }
            // 如果有Route元素的注解,并且处理过程中无异常则返回true
            return true;
        }
        // 否则返回false
        return false;
    }

parseRoutes()

这个函数的代码有点长,大家耐心看!

    // Consts.java

    public static final String ACTIVITY = "android.app.Activity";
    public static final String FRAGMENT = "android.app.Fragment";
    public static final String FRAGMENT_V4 = "android.support.v4.app.Fragment";
    public static final String SERVICE = "android.app.Service";

    private static final String FACADE_PACKAGE = "com.alibaba.android.arouter.facade";
    private static final String TEMPLATE_PACKAGE = ".template";

    public static final String IROUTE_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IRouteGroup";

    public static final String IPROVIDER_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IProviderGroup";
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
        if (CollectionUtils.isNotEmpty(routeElements)) {
            // ...

            rootMap.clear();

            // 获取ACTIVITY, SERVICE, FRAGMENT, FRAGMENT_V4 这四种 类型镜像
            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();

            // ARouter的接口
            TypeElement type_IRouteGroup = elements.getTypeElement(IROUTE_GROUP);
            TypeElement type_IProviderGroup = elements.getTypeElement(IPROVIDER_GROUP);

            // 
            // 下面就是遍历获取的注解信息,通过javapoet来生成类文件了 

            ClassName routeMetaCn = ClassName.get(RouteMeta.class);
            ClassName routeTypeCn = ClassName.get(RouteType.class);

            /*
               ParameterizedTypeName用来创建类型对象,例如下面

               ```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))
                    )
            );

            /*
                RouteMeta封装了路由相关的信息

              ```Map<String, RouteMeta>```
             */
            ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
                    ClassName.get(Map.class),
                    ClassName.get(String.class),
                    ClassName.get(RouteMeta.class)
            );

            /*
              创建输入参数
             */

            // 1。 生成的参数:Map<String, Class<? extends IRouteGroup>> routes
            ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build(); // 第一个参数表示参数类型,第二个函数表示参数名称

            // 2。 Map<String, RouteMeta> atlas
            ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();

            // 3。 Map<String, Rou
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值