Android架构师-组件化-APT生成路由动态参数类文件 8

上篇文章中,我们已经将路由数据创建完毕,这篇文章我们来解决这个问题以及动态参数如何传递?

首先我们要知道,我们的组件化之间已经能够完成动态跳转,那么跳转的时候可能会携带参数,那么这个注解处理器需要作用到接收方,让注解处理器帮我们处理参数获取的这部分代码,我们只要在想要获取的参数上面添加注解,就能够获取参数的值了。

那么里面需要注意的难点就是  :1:通过注解处理器动态扫描所有参数注解

                                                     2:获取到有参数注解的属性后,如何动态解析?解析后生成什么样的文件?

                                                     3:如何通过获取的参数值,给属性赋值呢?

我们看看

1:动态参数传递:

常规参数接收:

Android架构师-组件化-APT生成路由动态参数类文件 8_第1张图片

实现敏捷开发

最终的形态是下图的样式,

Android架构师-组件化-APT生成路由动态参数类文件 8_第2张图片

开始撸码:-

我们还是使用APT的技术,

1:首先在arouter_annotation中新建一个自定义注解

此注解是作用到属性上的,获取一个变量名字即可


@Target(ElementType.FIELD) // 该注解作用在属性之上
@Retention(RetentionPolicy.CLASS)//编译时注解(RetentionPolicy.CLASS),指@Retention(RetentionPolicy.CLASS)作用域class字节码上,生命周期只有在编译器间有效。
public @interface Parameter {

    // 不填写name的注解值表示该属性名就是key,填写了就用注解值作为key
    // 从getIntent()方法中获取传递参数值
    String name() default "";
}

2:在arouter_api中添加一个接口,用于扩展

完成赋值

/**
 * 参数Parameter加载接口
 */
public interface ParameterLoad {

    /**
     * 目标对象.属性名 = getIntent().属性类型("注解值or属性名");完成赋值
     *
     * @param target 目标对象,如:MainActivity(中的某些属性)
     */
    void loadParameter(Object target);
}

 

3:在arouter_compiler中添加一个对于Parameter注解的注解处理器

我们分步骤完成 

 1)新建一个注解处理器,添加注解支持的注解类型

public static final String PARAMETER_ANNOTATION_TYPES = "com.netease.arouter.annotation.Parameter";

2)完成APT中的工具类的初始化工作

/**
 * 编码此类1句话:细心再细心,出了问题debug真的不好调试
 */
@AutoService(Processor.class)
@SupportedAnnotationTypes({Constants.PARAMETER_ANNOTATION_TYPES})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ParameterProcessor extends AbstractProcessor {
 
 
    private Elements elementUtils;
    private Types typeUtils;
    private Messager messager;
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
        typeUtils = processingEnvironment.getTypeUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();
    }

  @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {

       return false;
}

}

3)重点完成process  处理自定义注解,生成需要的java类文件

                        2:获取到有参数注解的属性后,如何动态解析?解析后生成什么样的文件?

                       3:如何通过获取的参数值,给属性赋值呢?

上面的两个疑问,可以并到一个地方解决,我们生成的目标文件,无非就是要帮获取参数的Activity拿到传递过来的参数值,并且赋值。 

//模拟的需要的apt类文件
public class Order_MainActivity$$Parameter implements ParameterLoad {
  @Override
  public void loadParameter(Object target) {
    Order_MainActivity t = (Order_MainActivity)target;
    t.name = t.getIntent().getStringExtra("name");
  }
}
 @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {
        // 一旦有类之上使用@Parameter注解
        if (!EmptyUtils.isEmpty(set)) {
            // 获取所有被 @Parameter 注解的 元素(属性)集合
            Set elements = roundEnvironment.getElementsAnnotatedWith(Parameter.class);

            if (!EmptyUtils.isEmpty(elements)) {
                // 解析元素
                try {
                    // 赋值临时map存储,用来存放被注解的属性集合
                    valueOfParameterMap(elements);
                    // 生成类文件,如:
                    createParameterFile();
                    return true;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return true;
        }
        return false;
    }

这个地方需要注意一下,这是一个map集合,key:是获取的属性节点的父节点,

例如:com.netease.modular.order.Order_MainActivity      value:是当前父节点下的所有注解属性的集合


    // 临时map存储,用来存放被@Parameter注解的属性集合,生成类文件时遍历
    // key:类节点, value:被@Parameter注解的属性集合
    private Map> tempParameterMap = new HashMap<>();

/**
     * 赋值临时map存储,用来存放被@Parameter注解的属性集合,生成类文件时遍历
     *
     * @param elements 被 @Parameter 注解的 元素集合
     */
    private void valueOfParameterMap(Set elements) {
        for (Element element : elements) {
           // 注解在属性之上,属性节点父节点是类节点  获取包名
            TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
            // 如果map集合中的key:类节点存在,直接添加属性
            if (tempParameterMap.containsKey(enclosingElement)) {
                tempParameterMap.get(enclosingElement).add(element);
            } else {
                List fields = new ArrayList<>();
                fields.add(element);
                tempParameterMap.put(enclosingElement, fields);
            }
        }
    }

我们拿到所有被注解的参数集合 后 去循环,


    private void createParameterFile() throws IOException {
        // 判断是否有需要生成的类文件
        if (EmptyUtils.isEmpty(tempParameterMap)) return;
        // 通过Element工具类,获取Parameter类型
        TypeElement activityType = elementUtils.getTypeElement(Constants.ACTIVITY);
        TypeElement parameterType = elementUtils.getTypeElement(Constants.PARAMETER_LOAD);

        // 参数体配置(Object target)
        //ParameterSpect是JavaPoet包中的 
        ParameterSpec parameterSpec = ParameterSpec.builder(TypeName.OBJECT, Constants.PARAMETER_NAMR).build();
//第一层循环  循环的是临时集合中的数据
        for (Map.Entry> entry : tempParameterMap.entrySet()) {
            // Map集合中的key是类名,如:MainActivity
            TypeElement typeElement = entry.getKey();
            // 如果类名的类型和Activity类型不匹配
            if (!typeUtils.isSubtype(typeElement.asType(), activityType.asType())) {
                throw new RuntimeException("@Parameter注解目前仅限用于Activity类之上");
            }

            // 获取类名
            ClassName className = ClassName.get(typeElement);
            // 方法体内容构建
            ParameterFactory factory = new ParameterFactory.Builder(parameterSpec)
                    .setMessager(messager)
                    .setClassName(className)
                    .build();

            // 添加方法体内容的第一行
            factory.addFirstStatement();

            // 第二层循环  遍历类里面所有属性
            for (Element fieldElement : entry.getValue()) {
                factory.buildStatement(fieldElement);
            }

            // 最终生成的类文件名(类名$$Parameter)
            String finalClassName = typeElement.getSimpleName() + Constants.PARAMETER_FILE_NAME;
            messager.printMessage(Diagnostic.Kind.NOTE, "APT生成获取参数类文件:" +
                    className.packageName() + "." + finalClassName);

            // MainActivity$$Parameter
            JavaFile.builder(className.packageName(), // 包名
                    TypeSpec.classBuilder(finalClassName) // 类名
                            .addSuperinterface(ClassName.get(parameterType)) // 实现ParameterLoad接口
                            .addModifiers(Modifier.PUBLIC) // public修饰符
                            .addMethod(factory.build()) // 方法的构建(方法参数 + 方法体)
                            .build()) // 类构建完成
                    .build() // JavaFile构建完成
                    .writeTo(filer); // 文件生成器开始生成类文件
        }
    }

 //方法体创建  使用一个factory

import com.netease.arouter.annotation.Parameter;
import com.netease.arouter.compiler.utils.Constants;
import com.netease.arouter.compiler.utils.EmptyUtils;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;

import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;

public class ParameterFactory {

    // MainActivity t = (MainActivity) target;
    private static final String CONTENT = "$T t = ($T)target";

    // 方法体构建
    private MethodSpec.Builder methodBuidler;

    // Messager用来报告错误,警告和其他提示信息
    private Messager messager;

    // 类名,如:MainActivity
    private ClassName className;

    private ParameterFactory(Builder builder) {
        this.messager = builder.messager;
        this.className = builder.className;

        // 通过方法参数体构建方法体:public void loadParameter(Object target) {
        methodBuidler = MethodSpec.methodBuilder(Constants.PARAMETER_METHOD_NAME)
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(builder.parameterSpec);
    }

    /**
     * 添加方法体内容的第一行(MainActivity t = (MainActivity) target;)
     */
    public void addFirstStatement() {
        // 方法内容:MainActivity t = (MainActivity) target;
        methodBuidler.addStatement(CONTENT, className, className);
    }

    public MethodSpec build() {
        return methodBuidler.build();
    }

    /**
     * 构建方体内容,如:t.s = t.getIntent.getStringExtra("s");
     *
     * @param element 被注解的属性元素
     */
    public void buildStatement(Element element) {
        // 遍历注解的属性节点 生成函数体
        TypeMirror typeMirror = element.asType();
        // 获取 TypeKind 枚举类型的序列号
        int type = typeMirror.getKind().ordinal();
        // 获取属性名
        String fieldName = element.getSimpleName().toString();
        // 获取注解的值
        String annotationValue = element.getAnnotation(Parameter.class).name();
        // 判断注解的值为空的情况下的处理(注解中有name值就用注解值)
        annotationValue = EmptyUtils.isEmpty(annotationValue) ? fieldName : annotationValue;
        // 最终拼接的前缀:
        String finalValue = "t." + fieldName;
        // t.s = t.getIntent().
        String methodContent = finalValue + " = t.getIntent().";

        // TypeKind 枚举类型不包含String
        if (type == TypeKind.INT.ordinal()) {
            // t.s = t.getIntent().getIntExtra("age", t.age);
            methodContent += "getIntExtra($S, " + finalValue + ")";
        } else if (type == TypeKind.BOOLEAN.ordinal()) {
            // t.s = t.getIntent().getBooleanExtra("isSuccess", t.age);
            methodContent += "getBooleanExtra($S, " + finalValue + ")";
        } else {
            // t.s = t.getIntent.getStringExtra("s");
            if (typeMirror.toString().equalsIgnoreCase(Constants.STRING)) {
                methodContent += "getStringExtra($S)";
            }
        }

        // 健壮代码
        if (methodContent.endsWith(")")) {
            // 添加最终拼接方法内容语句
            methodBuidler.addStatement(methodContent, annotationValue);
        } else {
            messager.printMessage(Diagnostic.Kind.ERROR, "目前暂支持String、int、boolean传参");
        }
    }

    public static class Builder {

        // Messager用来报告错误,警告和其他提示信息
        private Messager messager;

        // 类名,如:MainActivity
        private ClassName className;

        // 方法参数体
        private ParameterSpec parameterSpec;

        public Builder(ParameterSpec parameterSpec) {
            this.parameterSpec = parameterSpec;
        }

        public Builder setMessager(Messager messager) {
            this.messager = messager;
            return this;
        }

        public Builder setClassName(ClassName className) {
            this.className = className;
            return this;
        }

        public ParameterFactory build() {
            if (parameterSpec == null) {
                throw new IllegalArgumentException("parameterSpec方法参数体为空");
            }

            if (className == null) {
                throw new IllegalArgumentException("方法内容中的className为空");
            }

            if (messager == null) {
                throw new IllegalArgumentException("messager为空,Messager用来报告错误、警告和其他提示信息");
            }

            return new ParameterFactory(this);
        }
    }
}

 

传参已经完成

 

 

你可能感兴趣的:(Android架构师-组件化-APT生成路由动态参数类文件 8)