ARouter源码解析(四)-- arouter-compiler模块分析(APT流程)

文章目录

  • @Router注解
    • BaseProcessor
    • RouteProcessor
      • parseRoutes

在ARouter中有很多自动生成的文件,其中可以看ARouter github上的demo项目,编译文成之后在 app->build->generated->source->apt->debug->comm.alibaba.android.arouter->routes目录下,有很多ARouter开头的文件,这些都是在编译时期通过APT技术生成的文件。这些文件都是通过注解的方式获取到类数据然后生成的,本章内容就是分析一下ARouter中 自定义注解处理器部分的逻辑。
arouter-compileprocess有三种:

  • AutowiredProcessor : 用来生成像 Test1Activity$$ARouter$$Autowired这种类型;
  • InterceptorProcessor : 用来生成像ARouter$$Interceptors$$app这种类型;
  • RouteProcessor: 用来生成像ARouter$$Root$$app,ARouter$$Providers$$appARouter$$Group$$test这种类型;
    本章主要分析RouteProcessor

@Router注解

首先看一下最常用的路由注解。在arouter-annotation模块下创建了Router注解文件。

/**
 * 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;
}
  • path定义的是路由类对应的路由地址,框架中就是通过这个值获取到对应的路由操作类。
  • group定义的就是标记路由类所处的组名,可以不用设置,默认根据path中的第一段路径命名。
  • name作用于文档中,实际开发过程中没什么作用。
  • extras可以理解为一个属性配置字段,通过设置不同的值来标记跳转页面需要做的特殊逻辑。
  • priority 暂未找到什么使用场景
    介绍完Route注解的含义,看一下其对应的注解处理器的逻辑。

BaseProcessor

首先看一下所有注解处理器的基类BaseProcessor

 @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        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);
            throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
        }
    }
  • logger都是常规的在APT中使用到的变量初始化。
  • options对象就是我们在每个module中声明的参数的集合
javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
            }
        }
  • moduleName 就是AROUTER_MODULE_NAME所对应的值
  • generateDocAROUTER_GENERATE_DOC所对应的值
  • 所以基类里面做的就是一些属性的初始化,和配置属性的获取。

RouteProcessor

先看RouteProcessor类上的几个注解

@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
	...
}
  • @AutoService会自动在META-INF文件夹下生成Process配置信息文件,避免手动配置。
  • @SupportedAnnotationTypes指定Processor处理的注解;

之后调用init方法。

@Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        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 = elementUtils.getTypeElement(Consts.IPROVIDER).asType();

        logger.info(">>> RouteProcessor init. <<<");
    }

这里会调用到BaseProcessinit方法,主要是一些对象的初始化,gradle中配置的参数获取和路由文档生成。
之后调用process方法。process就是最主要的处理注解生成代码的逻辑。

@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;
    }
  • Set routeElements = roundEnv.getElementsAnnotatedWith(Route.class); 获取@Route注解的集合
  • this.parseRoutes()中完成所有的逻辑处理,下面着重分析一下这里的代码。

parseRoutes

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();
        // Activity 类型
        TypeMirror type_Activity = elements.getTypeElement(ACTIVITY).asType();
        // Service 类型
        TypeMirror type_Service = elements.getTypeElement(SERVICE).asType();
        // Fragment 类型
        TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT).asType();
        // v4 Fragment 类型
        TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();
	
        // IRouteGroup 类型
        TypeElement type_IRouteGroup = elements.getTypeElement(IROUTE_GROUP);
        // IProviderGroup 类型
        TypeElement type_IProviderGroup = elements.getTypeElement(IPROVIDER_GROUP);
        // 获取 RouteMeta 和 RouteType 的类名
        ClassName routeMetaCn = ClassName.get(RouteMeta.class);
        ClassName routeTypeCn = ClassName.get(RouteType.class);
            /*
           构造 ARouter$$Root$$xxx 的 loadInto 方法入参类型
           Build input type, format as :
	
           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$$xxx 的 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!
	
        /*
          构造 ARouter$$Root$$xxx 的 loadInto 方法
          Build method : 'loadInto'
         */
        MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC)
                .addParameter(rootParamSpec);
	
        ...
        
	}
}

刚开始就是初始化一些对象,下面就是关键的处理逻辑了。

//  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;

    // 如果 element 修饰的类是 Activity 类型的
    if (types.isSubtype(tm, type_Activity)) {                 // Activity
        logger.info(">>> Found activity route: " + tm.toString() + " <<<");

        // 获取 Activity 中 @Autowired 注解的属性,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);
            }
        }
        // 构造 activity 类型的路由数据
        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)) { // fragment 类型
        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() + "].");
    }
    // 将生成好的 routeMeta 按组存放进入 groupMap 中
    categories(routeMeta);
}

主要是讲routeElement进行分类,将@Route修饰的类信息封装进RouteMeta中。之后把RouteMeta按照组名分好组存进groupMap中。

// 构造 ARouter$$Providers$$xxx 的 loadInto 方法
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .addParameter(providerParamSpec);

Map<String, List<RouteDoc>> docSource = new HashMap<>();

// 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();

    // 构造 ARouter$$Group$$xxx 的 loadInto 方法
    MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
            .addAnnotation(Override.class)
            .addModifiers(PUBLIC)
            .addParameter(groupParamSpec);

    List<RouteDoc> routeDocList = new ArrayList<>();

    // Build group method body
    Set<RouteMeta> groupData = entry.getValue();
    for (RouteMeta routeMeta : groupData) {
        RouteDoc routeDoc = extractDocInfo(routeMeta);
        // 类名。比如 com.alibaba.android.arouter.demo.testservice.HelloService
        ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());

        switch (routeMeta.getType()) {
            case PROVIDER:  // Need cache provider's super class
                // 获取该节点下的接口
                List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
                // 遍历接口
                for (TypeMirror tm : interfaces) {
                    routeDoc.addPrototype(tm.toString());
                    // 如果接口是 iProvider 类型
                    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)) { // 如果是 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;
        }

最后会生成:

providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));

之后

// 构造 RouteMeta 的 paramType 参数
    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();

    // 以下代码生成这种模版 atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap(){{put("ser", 9); }}, -1, -2147483648));

    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$$xxx 类
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);

上面代码主要做的事情就是遍历groupmap集合给ARouter$$Group$$xxx类中的loadInto添加方法体,并生成 java 文件。

if (MapUtils.isNotEmpty(rootMap)) {
    // Generate root meta by group name, it must be generated before root, then I can find out the class of group.
    // 生成 ARouter$$Root$$app 的 loadInto 方法体
    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()));
    }
}

// Output route doc
if (generateDoc) {
    docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
    docWriter.flush();
    docWriter.close();
}

// 生成 ARouter$$Providers$$app 类
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$$app 类 
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 + " <<<");

这就是RouteProcessor的整个流程,到此ARouter的源码分析系列就完成了。

对于文中有疑惑的地方,或者有任何意见和建议的地方都可以评论留言,我会第一时间回复~与君共勉。

你可能感兴趣的:(源码分析)