上篇文章中,我们已经将路由数据创建完毕,这篇文章我们来解决这个问题以及动态参数如何传递?
首先我们要知道,我们的组件化之间已经能够完成动态跳转,那么跳转的时候可能会携带参数,那么这个注解处理器需要作用到接收方,让注解处理器帮我们处理参数获取的这部分代码,我们只要在想要获取的参数上面添加注解,就能够获取参数的值了。
那么里面需要注意的难点就是 :1:通过注解处理器动态扫描所有参数注解
2:获取到有参数注解的属性后,如何动态解析?解析后生成什么样的文件?
3:如何通过获取的参数值,给属性赋值呢?
我们看看
1:动态参数传递:
常规参数接收:
实现敏捷开发
最终的形态是下图的样式,
开始撸码:-
我们还是使用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 extends TypeElement> 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 extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 一旦有类之上使用@Parameter注解
if (!EmptyUtils.isEmpty(set)) {
// 获取所有被 @Parameter 注解的 元素(属性)集合
Set extends Element> 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 extends Element> 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);
}
}
}
传参已经完成