ARouter源碼解析(四)

arouter-compiler version : 1.2.2

前言

之前對(duì) arouter-api 做了整個(gè)流程的分析,今天來(lái)看看 arouter-compiler 。

arouter-compiler 主要是利用 apt 在編譯期自動(dòng)生成代碼的。之前我們看到的 ARouter$$Root$$app 、 ARouter$$Group$$testTest1Activity$$ARouter$$Autowired 等都是 arouter-compiler 生成的。

那接下來(lái)就分析分析 arouter-compiler 是怎么生成這些源碼的。

arouter-compiler

arouter-compiler 中 processor 有三種:

  • AutowiredProcessor : 用來(lái)生成像 Test1Activity$$ARouter$$Autowired 這種類(lèi)型;
  • InterceptorProcessor : 用來(lái)生成像 ARouter$$Interceptors$$app 這種類(lèi)型;
  • RouteProcessor : 用來(lái)生成像 ARouter$$Root$$appARouter$$Providers$$appARouter$$Group$$test 這種類(lèi)型;

RouteProcessor

在這里我們就只分析 RouteProcessor 了。

RouteProcessor 相比其他兩個(gè) Processor 來(lái)說(shuō),代碼更長(zhǎng),邏輯更加復(fù)雜。并且 RouteProcessor 主要處理的是路由映射這一塊。其他兩個(gè) RouteProcessor 也是大同小異,有興趣的同學(xué)可以自行閱讀源碼。

先來(lái)看看 RouteProcessor 的定義:

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

RouteProcessor 類(lèi)上面的注解很多,我們一個(gè)一個(gè)來(lái)看:

  • @AutoService 會(huì)自動(dòng)在 META-INF 文件夾下生成 Processor 配置信息文件,避免手動(dòng)配置的麻煩;
  • @SupportedOptions 指定 Processor 支持的選項(xiàng)參數(shù)名稱(chēng),KEY_MODULE_NAME 就是 AROUTER_MODULE_NAME ,KEY_GENERATE_DOC_NAME 就是 AROUTER_GENERATE_DOC;沒(méi)錯(cuò),這兩個(gè)就是我們一開(kāi)始在 build.gradle 中配置的。
  • @SupportedSourceVersion 指定 Processor 支持的 JDK 的版本;
  • @SupportedAnnotationTypes 指定 Processor 處理的注解;

接著,趁熱打鐵。來(lái)瞧瞧 RouteProcessor 的 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.

    // 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.");
    }

    // 如果需要生成路由 doc
    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 = elements.getTypeElement(Consts.IPROVIDER).asType();

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

在 init 方法中,主要獲取了 KEY_MODULE_NAME 和 KEY_GENERATE_DOC_NAME 這兩個(gè)編譯選項(xiàng)參數(shù)。然后判斷一下是否需要生成路由文檔。

在 init 方法中獲取參數(shù)后,接著就是 process 方法。

process 方法就好像是 main 方法一樣,在這里面都是 processer 處理注解自動(dòng)生成代碼的邏輯。

@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... <<<");
            this.parseRoutes(routeElements);

        } catch (Exception e) {
            logger.error(e);
        }
        return true;
    }

    return false;
}

在 process 中調(diào)用了 parseRoutes ,parseRoutes 方法實(shí)在是太長(zhǎng)了,在這里我們進(jìn)行分段講解吧。

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 類(lèi)型
        TypeMirror type_Activity = elements.getTypeElement(ACTIVITY).asType();
        // Service 類(lèi)型
        TypeMirror type_Service = elements.getTypeElement(SERVICE).asType();
        // Fragment 類(lèi)型
        TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT).asType();
        // v4 Fragment 類(lèi)型
        TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();
    
        // IRouteGroup 類(lèi)型
        TypeElement type_IRouteGroup = elements.getTypeElement(IROUTE_GROUP);
        // IProviderGroup 類(lèi)型
        TypeElement type_IProviderGroup = elements.getTypeElement(IPROVIDER_GROUP);
        // 獲取 RouteMeta 和 RouteType 的類(lèi)名
        ClassName routeMetaCn = ClassName.get(RouteMeta.class);
        ClassName routeTypeCn = ClassName.get(RouteType.class);
    
        /*
           構(gòu)造 ARouter$$Root$$xxx 的 loadInto 方法入?yún)㈩?lèi)型
           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))
                )
        );
    
        /*
          構(gòu)造 ARouter$$Group$$xxx 的 loadInto 方法入?yún)㈩?lèi)型
          Map<String, RouteMeta>
         */
        ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ClassName.get(RouteMeta.class)
        );
    
        /*
          構(gòu)造方法入?yún)?shù)名稱(chēng)
          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!
    
        /*
          構(gòu)造 ARouter$$Root$$xxx 的 loadInto 方法
          Build method : 'loadInto'
         */
        MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC)
                .addParameter(rootParamSpec);
    
        ...
        
    }
}

parseRoutes 方法一開(kāi)始,做足了準(zhǔn)備。下面就到了放大招的時(shí)候了。

//  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 修飾的類(lèi)是 Activity 類(lèi)型的
    if (types.isSubtype(tm, type_Activity)) {                 // Activity
        logger.info(">>> Found activity route: " + tm.toString() + " <<<");

        // 獲取 Activity 中 @Autowired 注解的屬性,IProvider 類(lèi)型的除外
        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);
            }
        }
        // 構(gòu)造 activity 類(lèi)型的路由數(shù)據(jù)
        routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
        routeMeta.setInjectConfig(injectConfig);
    } else if (types.isSubtype(tm, iProvider)) {         // IProvider 類(lèi)型
        logger.info(">>> Found provider route: " + tm.toString() + " <<<");
        routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
    } else if (types.isSubtype(tm, type_Service)) {           // Service 類(lèi)型
        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 類(lèi)型
        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 按組存放進(jìn)入 groupMap 中
    categories(routeMeta);
}

上面這段代碼主要將每個(gè) routeElement 進(jìn)行了分類(lèi),將 @Route 修飾的類(lèi)信息封裝進(jìn) RouteMeta 中。再把 RouteMeta 按照組名分好組存進(jìn) groupMap 中。

// 構(gòu)造 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();

    // 構(gòu)造 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);
        // 類(lèi)名。比如 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
                // 獲取該節(jié)點(diǎn)下的接口
                List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
                // 遍歷接口
                for (TypeMirror tm : interfaces) {
                    routeDoc.addPrototype(tm.toString());
                    // 如果接口是 iProvider 類(lèi)型
                    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;
        }

上面的代碼最終會(huì)生成 ARouterProvidersxxx 的 loadInto 方法,比如像這樣:

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

那我們接著看。

    // 構(gòu)造 RouteMeta 的 paramType 參數(shù)
    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<String, Integer>(){{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<String, Integer>(){{" + 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 類(lèi)
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 集合給 ARouterGroupxxx 類(lèi)中的 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 類(lèi)
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 類(lèi) 
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 + " <<<");

以上,就是整個(gè) RouteProcessor 的流程??赐?RouteProcessor 之后,相信你對(duì) ARouter 的的了解也更加深入了。

之后,也會(huì)對(duì) ARouter 的 arouter-register 模塊做一個(gè)深入解析,敬請(qǐng)期待吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容