Bootstrap

ARoute源码解读之自动生成路由映射文件

在开始ARoute源码分析之前需要先了解Javapoet和APT分别是什么?

JavaPoet

官方链接:https://github.com/square/javapoet
A Java API for generating .java source files.
Source file generation can be useful when doing things such as annotation processing or interacting with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate the need to write boilerplate while also keeping a single source of truth for the metadata.

以上是JavaPoet的官方定义,大概总结如下:

  • JavaPoet是一款可以自动生成Java文件的第三方依赖;
  • 简洁易懂的API,上手快;
  • 自动化生成繁杂、重复的Java文件,提高工作效率,简化流程。

APT

是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。

ARouter通过APT在编译期扫描和处理注解,使用JavaPoet生成模板代码,最终生成相关路由信息类。例如下面3个类:
在这里插入图片描述
接下来就从ARoute源码中分析这三个文件具体是如何生成的。
首先从使用上开始入手,在项目中使用ARoute时,需要在每个使用到ARoute的module下的build.gradle文件中添加如下依赖就是为了满足ARoute通过APT在编译期扫描并处理带有指定注解的地方:

android {
   
  javaCompileOptions {
   
            annotationProcessorOptions {
   
                arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
            }
        }
}

dependencies {
   
	annotationProcessor'com.alibaba:arouter-compiler:1.1.4'
}

源码所在位置在aroute-compiler中:
在这里插入图片描述

首先看一下BaseProcessor:

public abstract class BaseProcessor extends AbstractProcessor {
   
    Filer mFiler;
    Logger logger;
    Types types;
    Elements elementUtils;
    TypeUtils typeUtils;
    // Module name, maybe its 'app' or others
    String moduleName = null;
    // If need generate router doc
    boolean generateDoc;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
   
        super.init(processingEnv);
		//ProcessingEnvironment 提供写文件、报告错误、查找工具类等功能
        mFiler = processingEnv.getFiler();
        types = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
        typeUtils = new TypeUtils(types, elementUtils);
        logger = new Logger(processingEnv.getMessager());

        // 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(NO_MODULE_NAME_TIPS);
            //使用ARouter框架时,出现异常,可以基于源码发现问题所在。
            throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
        }
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
   
        //返回所支持的Java版本为最新所支持的版本
        return SourceVersion.latestSupported();
    }

    @Override
    public Set<String> getSupportedOptions() {
   
        return new HashSet<String>() {
   {
   
            this.add(KEY_MODULE_NAME);
            this.add(KEY_GENERATE_DOC_NAME);
        }};
    }
}

定义BaseProcessor继承自AbstractProcessor,初始化了一些变量,获取并检查模块名。

生产模板代码:RouteProcessor的源码实现

RouteProcessor继承BaseProcessor,重写了关键的方法process(),在此方法内使用JavaPoet实现了模板代码的编写:

 @Override
    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;
    }

    private void parseRoutes(Set<? extends Element> routeElements) throws IOExc
;