ARouter 源码历险记 (二)

目录

ARouter 源码历险记 (一)

ARouter 源码历险记 (二)

ARouter 源码历险记 (三)

ARouter 源码历险记 (四)

ARouter 源码历险记 (五)

 

 

InterceptorProcessor

    上一篇,详细分析了RouteProcessor的内容,该注解处理器的主要作用就是收集@Route的信息。代码比较长,如果你耐心研究完了,那么恭喜,剩下的两个Processor的解读将会一马平川。

    首先看init方法,这里就真心不复制粘贴代码了,因为代码几乎和RouteProcessor的init方法一模一样,区别只是如下代码

iInterceptor = elementUtil.getTypeElement(Consts.IINTERCEPTOR).asType();

获取了IInterceptor接口的节点。

    process方法同样会进一步调用processInterceptor方法进行真正的处理。

private void parseInterceptors(Set<? extends Element> elements) throws IOException {

        ......

            // Verify and cache, sort incidentally.
            for (Element element : elements) {
                if (verify(element)) {  // Check the interceptor meta
                    logger.info("A interceptor verify over, its " + element.asType());
                    Interceptor interceptor = element.getAnnotation(Interceptor.class);

                    Element lastInterceptor = interceptors.get(interceptor.priority());
                    if (null != lastInterceptor) { // Added, throw exceptions
                        throw new IllegalArgumentException(
                                String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",
                                        interceptor.priority(),
                                        lastInterceptor.getSimpleName(),
                                        element.getSimpleName())
                        );
                    }

                    interceptors.put(interceptor.priority(), element);
                } else {
                    logger.error("A interceptor verify failed, its " + element.asType());
                }
            }

            ......

    }

    
    private boolean verify(Element element) {
        Interceptor interceptor = element.getAnnotation(Interceptor.class);
        // It must be implement the interface IInterceptor and marked with annotation Interceptor.
        return null != interceptor && ((TypeElement) element).getInterfaces().contains(iInterceptor);
    }

    遍历所有节点,如果该节点没有有Interceptor注解,或者节点没有直接或者间接继承了IInterceptor接口,那么直接跳过。否则提取@Interceptor对象,如果设置的优先级已经存在,那么就会报错。所以可见,Interceptor的优先级不能重复值。如果不存在,那么存入map中

private void parseInterceptors(Set<? extends Element> elements) throws IOException {
        if (CollectionUtils.isNotEmpty(elements)) {

            ......

            // Interface of ARouter.
            TypeElement type_ITollgate = elementUtil.getTypeElement(IINTERCEPTOR);
            TypeElement type_ITollgateGroup = elementUtil.getTypeElement(IINTERCEPTOR_GROUP);

            /**
             *  Build input type, format as :
             *
             *  ```Map<Integer, Class<? extends ITollgate>>```
             */
            ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(
                    ClassName.get(Map.class),
                    ClassName.get(Integer.class),
                    ParameterizedTypeName.get(
                            ClassName.get(Class.class),
                            WildcardTypeName.subtypeOf(ClassName.get(type_ITollgate))
                    )
            );

            // Build input param name.
            ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();

            // Build method : 'loadInto'
            MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(tollgateParamSpec);

            // Generate
            if (null != interceptors && interceptors.size() > 0) {
                // Build method body
                for (Map.Entry<Integer, Element> entry : interceptors.entrySet()) {
                    loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));
                }
            }

            // Write to disk(Write file even interceptors is empty.)
            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                    TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName)
                            .addModifiers(PUBLIC)
                            .addJavadoc(WARNING_TIPS)
                            .addMethod(loadIntoMethodOfTollgateBuilder.build())
                            .addSuperinterface(ClassName.get(type_ITollgateGroup))
                            .build()
            ).build().writeTo(mFiler);

            logger.info(">>> Interceptor group write over. <<<");
        }
    }

    即使是在实际使用中,也很难看到比这个更简单清晰的processor。

    生成一个 @Override public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {} 方法。

    如果上一步保存的map不为空,那么遍历之,每个元素都在其中添加类似如下代码    

    interceptors.put(interceptor的优先级, 被注解的元素类);

    在包com.alibaba.android.arouter.routes中生成一个java文件,类名为ARoute$$Interceptors$$XXX(xxx为传入的moduleName),并且实现了接口IInterrecptorGroup。

    在ARouter中

   164901_0uHx_2398062.png

    app 模块生成了一个Interceptor类。

172039_eYeJ_2398062.png

    test-module-1模块也生成了一个Interceptor类。

 

AutowiredProcessor

init方法简单到一个字都不想多提。直接看process方法

@Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        if (CollectionUtils.isNotEmpty(set)) {
            try {
                logger.info(">>> Found autowired field, start... <<<");
                categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
                generateHelper();

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

        return false;
    }

首先调用了categories方法

private void categories(Set<? extends Element> elements) throws IllegalAccessException {
        if (CollectionUtils.isNotEmpty(elements)) {
            for (Element element : elements) {
                TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

                if (element.getModifiers().contains(Modifier.PRIVATE)) {
                    throw new IllegalAccessException("The autowired fields CAN NOT BE 'private'!!! please check field ["
                            + element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
                }

                if (parentAndChild.containsKey(enclosingElement)) { // Has categries
                    parentAndChild.get(enclosingElement).add(element);
                } else {
                    List<Element> childs = new ArrayList<>();
                    childs.add(element);
                    parentAndChild.put(enclosingElement, childs);
                }
            }

            logger.info("categories finished.");
        }
    }

     遍历所有被注解的节点,通过getEnclosingElement方法获取该节点所在的类的节点。(请注意,这样的说法是错误的。查看getEnclosingElement的方法可以发现,当element是TypeParameterElement时,才是返回所在类的节点。因为Autowired是用来注解类内字段的,所以相当于放回类节点。)

    如果被注解的节点是private的将会直接报错。然后将所有节点根据所在类为key放入一个Map中:

     HashMap<节点所在类,Set<节点>> parentAndChild

    综上,其实categories的作用就是根据类将被注解的字段进行分类。很符合方法名嘛!

    再来看generateHelper方法:

private void generateHelper() throws IOException, IllegalAccessException {
        TypeElement type_ISyringe = elementUtil.getTypeElement(ISYRINGE);
        TypeMirror iProvider = elementUtil.getTypeElement(Consts.IPROVIDER).asType();
        TypeMirror activityTm = elementUtil.getTypeElement(Consts.ACTIVITY).asType();
        TypeMirror fragmentTm = elementUtil.getTypeElement(Consts.FRAGMENT).asType();
        TypeMirror fragmentTmV4 = elementUtil.getTypeElement(Consts.FRAGMENT_V4).asType();

        // Build input param name.
        ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();

        if (MapUtils.isNotEmpty(parentAndChild)) {
            for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
                // Build method : 'inject'
                MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
                        .addAnnotation(Override.class)
                        .addModifiers(PUBLIC)
                        .addParameter(objectParamSpec);

                TypeElement parent = entry.getKey();
                List<Element> childs = entry.getValue();

                String qualifiedName = parent.getQualifiedName().toString();
                String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
                String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;

                logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");

                injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));

                
            ......

    }

    遍历上一步存储的parentAndChild中的所有的entry。之后的操作相当于为每一个存在Autowired注解字段的类(即 Key)都创建一个java类,用来进行自动的注入!

    在该java类中,首先创建了一个inject方法, @Override public void inject(Object target)。然后在其中插入第一句代码:

    XXX substitute = (XXX)target;    将target强转成Key所代表的类的实例。

继续往下走,接下去的代码就是向方法中添加注入代码了。

private void generateHelper() throws IOException, IllegalAccessException {

        ...............


                // Generate method body, start inject.
                for (Element element : childs) {
                    Autowired fieldConfig = element.getAnnotation(Autowired.class);
                    String fieldName = element.getSimpleName().toString();
                    if (typeUtil.isSubtype(element.asType(), iProvider)) {  // It's provider
                        if ("".equals(fieldConfig.name())) {    // User has not set service path, then use byType.

                            // Getter
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
                                    ARouterClass,
                                    ClassName.get(element.asType())
                            );
                        } else {    // use byName
                            // Getter
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation();",
                                    ClassName.get(element.asType()),
                                    ARouterClass,
                                    fieldConfig.name()
                            );
                        }

                        // Validater
                        if (fieldConfig.required()) {
                            injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
                            injectMethodBuilder.addStatement(
                                    "throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
                            injectMethodBuilder.endControlFlow();
                        }
                    } 

                    .........

    }

    遍历Key所对应的Set(记录所有该类中包含的autowired字段),如果该字段的类型是IProvider或者其子类,那么他的注入方式和其他注入方式有些区别。

    如果@Autowired没有设置name属性,那么使用byType(根据类型生成)的注入方式,在代码中添加如下代码:

    substitute.XXX/*XXX表示当前element的字段名*/ = ARouter.getInstance().navigation(XXX/*element的字段类型*/.class);

    如果设置了name属性,那么久通过byName的方式注入代码:

    substitute.XXX/*XXX表示当前element的字段名*/ = (XXX/*element的字段类型*/)ARouter.getInstance().build(name属性).navigation();

    最后如果该Autowired字段标记了required,并且还是为空(即注入内容为空),那么就会添加报错语句,提示注入错误。

    关于IProvider的意义和这样做的原因,相信会随着源码的深入逐渐了解。

    之后是其他类型的注入

    

else {    // It's normal intent value
                        String statment = "substitute." + fieldName + " = substitute.";
                        boolean isActivity = false;
                        if (typeUtil.isSubtype(parent.asType(), activityTm)) {  // Activity, then use getIntent()
                            isActivity = true;
                            statment += "getIntent().";
                        } else if (typeUtil.isSubtype(parent.asType(), fragmentTm) || typeUtil.isSubtype(parent.asType(), fragmentTmV4)) {   // Fragment, then use getArguments()
                            statment += "getArguments().";
                        } else {
                            throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
                        }

                        statment = buildStatement(statment, TypeUtils.typeExchange(element.asType()), isActivity);
                        injectMethodBuilder.addStatement(statment, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());

                        // Validater
                        if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) {  // Primitive wont be check.
                            injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
                            injectMethodBuilder.addStatement(
                                    "throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
                            injectMethodBuilder.endControlFlow();
                        }
                    }

接下去是语句的拼装,以上代码最重要的是透露了一个消息,使用@Autowired注解字段的类必须是activity或者fragment的子类。因为注入的实现实际上就是通过getIntent().getXXX 或者 getArguments().getXXX获取内容,自动填入字段。

另外需要注意的一点是buildStatement方法:

private String buildStatement(String statment, int type, boolean isActivity) {
        if (type == TypeKind.BOOLEAN.ordinal()) {
            statment += (isActivity ? ("getBooleanExtra($S, false)") : ("getBoolean($S)"));
        } else if (type == TypeKind.BYTE.ordinal()) {
            statment += (isActivity ? ("getByteExtra($S, (byte) 0)") : ("getByte($S)"));
        } else if (type == TypeKind.SHORT.ordinal()) {
            statment += (isActivity ? ("getShortExtra($S, (short) 0)") : ("getShort($S)"));
        } else if (type == TypeKind.INT.ordinal()) {
            statment += (isActivity ? ("getIntExtra($S, 0)") : ("getInt($S)"));
        } else if (type == TypeKind.LONG.ordinal()) {
            statment += (isActivity ? ("getLongExtra($S, 0)") : ("getLong($S)"));
        } else if (type == TypeKind.FLOAT.ordinal()) {
            statment += (isActivity ? ("getFloatExtra($S, 0)") : ("getFloat($S)"));
        } else if (type == TypeKind.DOUBLE.ordinal()) {
            statment += (isActivity ? ("getDoubleExtra($S, 0)") : ("getDouble($S)"));
        } else if (type == TypeKind.OTHER.ordinal()) {
            statment += (isActivity ? ("getStringExtra($S)") : ("getString($S)"));
        }

        return statment;
    }

    所列出的内容就表示getXXX所支持的方法,发现没有序列化和数组的支持。是不是意味着不能注入对象以及List等。(高手指正下我分析的对不对……)

    然后还是@Autowired.required的验证性判断。

最终,生成java文件:

// Generate autowired helper
                JavaFile.builder(packageName,
                        TypeSpec.classBuilder(fileName)
                                .addJavadoc(WARNING_TIPS)
                                .addSuperinterface(ClassName.get(type_ISyringe))
                                .addModifiers(PUBLIC)
                                .addMethod(injectMethodBuilder.build())
                                .build()
                ).build().writeTo(mFiler);

    和其他两个processor有些不容,它生成的java文件的包名为当前key表示的类所在的包名,而不是放在arouter目录下,类名为XXX(key表示的类的类名)$$ARouter$$Autowired. 实现ISyringe接口。

    查找了下,在demo中只有app目录下有如下文件生成

180512_vn0Y_2398062.png

    打开代码看一下,还是符合上述分析的。

总结

    至此,编译阶段的准备工作算是结束了,剩下的代码运行都是在运行阶段,下一篇将会跟着源码的init方法一步分析下去。

 

转载于:https://my.oschina.net/zzxzzg/blog/863331

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值