7.ARouter的实现
1.Path与Group的映射关系
有 app,order,personal,以personal为例来讲解, 非常析细的关系
1.先生成path
public class ARouter$$Path$$personal implements ARouterPath {
@Override
public Map<String, RouterBean> getPathMap() {
Map<String, RouterBean> pathMap = new HashMap<>();
pathMap.put("/personal/Personal_Main2Activity", RouterBean.create();
pathMap.put("/personal/Personal_MainActivity", RouterBean.create());
return pathMap;
}
}
2.再生成Group
public class ARouter$$Group$$personal implements ARouterGroup {
@Override
public Map<String, Class<? extends ARouterPath>> getGroupMap() {
Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
groupMap.put("personal", ARouter$$Path$$personal.class);
return groupMap;
}
2.Path生成管理
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import com.xiangxue.arouter_annotation.ARouter;
import com.xiangxue.arouter_annotation.bean.RouterBean;
import com.xiangxue.arouter_compiler.utils.ProcessorConfig;
import com.xiangxue.arouter_compiler.utils.ProcessorUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
/**
* 同学们注意:编码此类,记住就是一个字(细心,细心,细心),出了问题debug真的不好调试
*/
// AutoService则是固定的写法,加个注解即可
// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册
// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)
// 允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({ProcessorConfig.AROUTER_PACKAGE})
// 指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
// 注解处理器接收的参数
@SupportedOptions({ProcessorConfig.OPTIONS, ProcessorConfig.APT_PACKAGE})
public class ARouterProcessor extends AbstractProcessor {
// 操作Element的工具类(类,函数,属性,其实都是Element)
private Elements elementTool;
// type(类信息)的工具类,包含用于操作TypeMirror的工具方法
private Types typeTool;
// Message用来打印 日志相关信息
private Messager messager;
// 文件生成器, 类 资源 等,就是最终要生成的文件 是需要Filer来完成的
private Filer filer;
private String options; // 各个模块传递过来的模块名 例如:app order personal
private String aptPackage; // 各个模块传递过来的目录 用于统一存放 apt生成的文件
// 仓库一 Path 缓存一
// Map<"personal", List<RouterBean>>
private Map<String, List<RouterBean>> mAllPathMap = new HashMap<>(); // 目前是一个
// 仓库二 Group 缓存二
// Map<"personal", "ARouter$$Path$$personal.class">
private Map<String, String> mAllGroupMap = new HashMap<>();
// 做初始化工作,就相当于 Activity中的 onCreate函数一样的作用
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementTool = processingEnvironment.getElementUtils();
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
typeTool = processingEnvironment.getTypeUtils();
// 只有接受到 App壳 传递过来的书籍,才能证明我们的 APT环境搭建完成
options = processingEnvironment.getOptions().get(ProcessorConfig.OPTIONS);
aptPackage = processingEnvironment.getOptions().get(ProcessorConfig.APT_PACKAGE);
messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>>>>> options:" + options);
messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>>>>> aptPackage:" + aptPackage);
if (options != null && aptPackage != null) {
messager.printMessage(Diagnostic.Kind.NOTE, "APT 环境搭建完成....");
} else {
messager.printMessage(Diagnostic.Kind.NOTE, "APT 环境有问题,请检查 options 与 aptPackage 为null...");
}
}
/**
* 相当于main函数,开始处理注解
* 注解处理器的核心方法,处理具体的注解,生成Java文件
*
* @param set 使用了支持处理注解的节点集合
* @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找的注解。
* @return true 表示后续处理器不会再处理(已经处理完成)
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set.isEmpty()) {
messager.printMessage(Diagnostic.Kind.NOTE, "并没有发现 被@ARouter注解的地方呀");
return false; // 没有机会处理
}
// 获取所有被 @ARouter 注解的 元素集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
// 通过Element工具类,获取Activity,Callback类型
TypeElement activityType = elementTool.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE);
// 显示类信息(获取被注解的节点,类节点)这也叫自描述 Mirror
TypeMirror activityMirror = activityType.asType();
// 遍历所有的类节点
for (Element element : elements) {
// 获取类节点,获取包节点 (com.xiangxue.xxxxxx)
// String packageName = elementTool.getPackageOf(element).getQualifiedName().toString();
// 获取简单类名,例如:MainActivity
String className = element.getSimpleName().toString();
messager.printMessage(Diagnostic.Kind.NOTE, "被@ARetuer注解的类有:" + className); // 打印出 就证明APT没有问题
// 拿到注解
ARouter aRouter = element.getAnnotation(ARouter.class);
// 下面是练习 JavaPoet
/**
* package com.example.helloworld;
*
* public final class HelloWorld {
* public static void main(String[] args) {
* System.out.println("Hello, JavaPoet!");
* }
* }
*/
// 1.方法
/*MethodSpec mainMethod = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
// 2.类
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(mainMethod)
.build();
// 3.包
JavaFile packagef = JavaFile.builder("com.derry.study", helloWorld).build();
// 去生成
try {
packagef.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.NOTE, "生成失败,请检查代码...");
}*/
// 先JavaPoet 写一个简单示例,方法--->类--> 包,是倒序写的思路哦
/*
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
*/
// 方法
/*MethodSpec mainMethod = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(void.class)
.addParameter(System[].class, "args")
// 增加main方法里面的内容
.addStatement("$T.out.println($S)", System.class, "AAAAAAAAAAA!")
.build();
// 类 Testapp Testorder
TypeSpec testClass = TypeSpec.classBuilder("Test" + options)
.addMethod(mainMethod)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.build();
// 包
JavaFile packagef = JavaFile.builder("com.xiangxue.test22", testClass).build();
try {
packagef.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.NOTE, "生成Test文件时失败,异常:" + e.getMessage());
}*/
// TODO 一系列的检查工作
// 在循环里面,对 “路由对象” 进行封装
RouterBean routerBean = new RouterBean.Builder()
.addGroup(aRouter.group())
.addPath(aRouter.path())
.addElement(element)
.build();
// ARouter注解的类 必须继承 Activity
TypeMirror elementMirror = element.asType(); // Main2Activity的具体详情 例如:继承了 Activity
if (typeTool.isSubtype(elementMirror, activityMirror)) { // activityMirror android.app.Activity描述信息
routerBean.setTypeEnum(RouterBean.TypeEnum.ACTIVITY); // 最终证明是 Activity
} else { // Derry.java 的干法 就会抛出异常
// 不匹配抛出异常,这里谨慎使用!考虑维护问题
throw new RuntimeException("@ARouter注解目前仅限用于Activity类之上");
}
if (checkRouterPath(routerBean)) {
messager.printMessage(Diagnostic.Kind.NOTE, "RouterBean Check Success:" + routerBean.toString());
// 赋值 mAllPathMap 集合里面去
List<RouterBean> routerBeans = mAllPathMap.get(routerBean.getGroup());
// 如果从Map中找不到key为:bean.getGroup()的数据,就新建List集合再添加进Map
if (ProcessorUtils.isEmpty(routerBeans)) { // 仓库一 没有东西
routerBeans = new ArrayList<>();
routerBeans.add(routerBean);
mAllPathMap.put(routerBean.getGroup(), routerBeans);// 加入仓库一
} else {
routerBeans.add(routerBean);
}
} else { // ERROR 编译期发生异常
messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解未按规范配置,如:/app/MainActivity");
}
} // TODO end for 同学们注意:在循环外面了 (此循环结束后,仓库一 缓存一 就存好所有 Path值了)
// mAllPathMap 里面有值了
// 定义(生成类文件实现的接口) 有 Path Group
TypeElement pathType = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_PATH); // ARouterPath描述
TypeElement groupType = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_GROUP); // ARouterGroup描述
// TODO 第一大步:系列PATH
try {
createPathFile(pathType); // 生成 Path类
} catch (IOException e) {
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.NOTE, "在生成PATH模板时,异常了 e:" + e.getMessage());
}
// TODO 第二大步:组头(带头大哥)
try {
createGroupFile(groupType, pathType);
} catch (IOException e) {
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.NOTE, "在生成GROUP模板时,异常了 e:" + e.getMessage());
}
return true; // 坑:必须写返回值,表示处理@ARouter注解完成
}
/**
* 生成路由组Group文件,如:ARouter$$Group$$app
* @param groupType ARouterLoadGroup接口信息
* @param pathType ARouterLoadPath接口信息
*/
private void createGroupFile(TypeElement groupType, TypeElement pathType) throws IOException {
// 仓库二 缓存二 判断是否有需要生成的类文件
if (ProcessorUtils.isEmpty(mAllGroupMap) || ProcessorUtils.isEmpty(mAllPathMap)) return;
// 返回值 这一段 Map<String, Class<? extends ARouterPath>>
TypeName methodReturns = ParameterizedTypeName.get(
ClassName.get(Map.class), // Map
ClassName.get(String.class), // Map<String,
// Class<? extends ARouterPath>> 难度
ParameterizedTypeName.get(ClassName.get(Class.class),
// ? extends ARouterPath
WildcardTypeName.subtypeOf(ClassName.get(pathType))) // ? extends ARouterLoadPath
// WildcardTypeName.supertypeOf() 做实验 ? super
// 最终的:Map<String, Class<? extends ARouterPath>>
);
// 1.方法 public Map<String, Class<? extends ARouterPath>> getGroupMap() {
MethodSpec.Builder methodBuidler = MethodSpec.methodBuilder(ProcessorConfig.GROUP_METHOD_NAME) // 方法名
.addAnnotation(Override.class) // 重写注解 @Override
.addModifiers(Modifier.PUBLIC) // public修饰符
.returns(methodReturns); // 方法返回值
// Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
methodBuidler.addStatement("$T<$T, $T> $N = new $T<>()",
ClassName.get(Map.class),
ClassName.get(String.class),
// Class<? extends ARouterPath> 难度
ParameterizedTypeName.get(ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(pathType))), // ? extends ARouterPath
ProcessorConfig.GROUP_VAR1,
ClassName.get(HashMap.class));
// groupMap.put("personal", ARouter$$Path$$personal.class);
// groupMap.put("order", ARouter$$Path$$order.class);
for (Map.Entry<String, String> entry : mAllGroupMap.entrySet()) {
methodBuidler.addStatement("$N.put($S, $T.class)",
ProcessorConfig.GROUP_VAR1, // groupMap.put
entry.getKey(), // order, personal ,app
ClassName.get(aptPackage, entry.getValue()));
}
// return groupMap;
methodBuidler.addStatement("return $N", ProcessorConfig.GROUP_VAR1);
// 最终生成的类文件名 ARouter$$Group$$ + personal
String finalClassName = ProcessorConfig.GROUP_FILE_NAME + options;
messager.printMessage(Diagnostic.Kind.NOTE, "APT生成路由组Group类文件:" +
aptPackage + "." + finalClassName);
// 生成类文件:ARouter$$Group$$app
JavaFile.builder(aptPackage, // 包名
TypeSpec.classBuilder(finalClassName) // 类名
.addSuperinterface(ClassName.get(groupType)) // 实现ARouterLoadGroup接口 implements ARouterGroup
.addModifiers(Modifier.PUBLIC) // public修饰符
.addMethod(methodBuidler.build()) // 方法的构建(方法参数 + 方法体)
.build()) // 类构建完成
.build() // JavaFile构建完成
.writeTo(filer); // 文件生成器开始生成类文件
}
/**
* 系列Path的类 生成工作
* @param pathType ARouterPath 高层的标准
* @throws IOException
*/
private void createPathFile(TypeElement pathType) throws IOException {
// 判断 map仓库中,是否有需要生成的文件
if (ProcessorUtils.isEmpty(mAllPathMap)) {
return; // 连缓存一 仓库一 里面 值都没有 不用干活了
}
// 倒序生成代码
// 任何的class类型,必须包装
// Map<String, RouterBean>
TypeName methodReturn = ParameterizedTypeName.get(
ClassName.get(Map.class), // Map
ClassName.get(String.class), // Map<String,
ClassName.get(RouterBean.class) // Map<String, RouterBean>
);
// 遍历仓库 app,order,personal
for (Map.Entry<String, List<RouterBean>> entry : mAllPathMap.entrySet()) { // personal
// 1.方法
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.PATH_METHOD_NAME)
.addAnnotation(Override.class) // 给方法上添加注解 @Override
.addModifiers(Modifier.PUBLIC) // public修饰符
.returns(methodReturn) // 把Map<String, RouterBean> 加入方法返回
;
// Map<String, RouterBean> pathMap = new HashMap<>(); // $N == 变量 为什么是这个,因为变量有引用 所以是$N
methodBuilder.addStatement("$T<$T, $T> $N = new $T<>()",
ClassName.get(Map.class), // Map
ClassName.get(String.class), // Map<String,
ClassName.get(RouterBean.class), // Map<String, RouterBean>
ProcessorConfig.PATH_VAR1, // Map<String, RouterBean> pathMap
ClassName.get(HashMap.class) // Map<String, RouterBean> pathMap = new HashMap<>();
);
// 必须要循环,因为有多个
// pathMap.put("/personal/Personal_Main2Activity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY,
// Personal_Main2Activity.class);
// pathMap.put("/personal/Personal_MainActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY));
List<RouterBean> pathList = entry.getValue();
/**
$N == 变量 变量有引用 所以 N
$L == TypeEnum.ACTIVITY
*/
// personal 的细节
for (RouterBean bean : pathList) {
methodBuilder.addStatement("$N.put($S, $T.create($T.$L, $T.class, $S, $S))",
ProcessorConfig.PATH_VAR1, // pathMap.put
bean.getPath(), // "/personal/Personal_Main2Activity"
ClassName.get(RouterBean.class), // RouterBean
ClassName.get(RouterBean.TypeEnum.class), // RouterBean.Type
bean.getTypeEnum(), // 枚举类型:ACTIVITY
ClassName.get((TypeElement) bean.getElement()), // MainActivity.class Main2Activity.class
bean.getPath(), // 路径名
bean.getGroup() // 组名
);
} // TODO end for
// return pathMap;
methodBuilder.addStatement("return $N", ProcessorConfig.PATH_VAR1);
// TODO 注意:不能像以前一样,1.方法,2.类 3.包, 因为这里面有implements ,所以 方法和类要合为一体生成才行,这是特殊情况
// 最终生成的类文件名 ARouter$$Path$$personal
String finalClassName = ProcessorConfig.PATH_FILE_NAME + entry.getKey();
messager.printMessage(Diagnostic.Kind.NOTE, "APT生成路由Path类文件:" +
aptPackage + "." + finalClassName);
// 生成类文件:ARouter$$Path$$personal
JavaFile.builder(aptPackage, // 包名 APT 存放的路径
TypeSpec.classBuilder(finalClassName) // 类名
.addSuperinterface(ClassName.get(pathType)) // 实现ARouterLoadPath接口 implements ARouterPath==pathType
.addModifiers(Modifier.PUBLIC) // public修饰符
.addMethod(methodBuilder.build()) // 方法的构建(方法参数 + 方法体)
.build()) // 类构建完成
.build() // JavaFile构建完成
.writeTo(filer); // 文件生成器开始生成类文件
// 仓库二 缓存二 非常重要一步,注意:PATH 路径文件生成出来了,才能赋值路由组mAllGroupMap
mAllGroupMap.put(entry.getKey(), finalClassName);
}
}
/**
* 校验@ARouter注解的值,如果group未填写就从必填项path中截取数据
* @param bean 路由详细信息,最终实体封装类
*/
private final boolean checkRouterPath(RouterBean bean) {
String group = bean.getGroup(); // 同学们,一定要记住: "app" "order" "personal"
String path = bean.getPath(); // 同学们,一定要记住: "/app/MainActivity" "/order/Order_MainActivity" "/personal/Personal_MainActivity"
// 校验
// @ARouter注解中的path值,必须要以 / 开头(模仿阿里Arouter规范)
if (ProcessorUtils.isEmpty(path) || !path.startsWith("/")) {
messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的path值,必须要以 / 开头");
return false;
}
// 比如开发者代码为:path = "/MainActivity",最后一个 / 符号必然在字符串第1位
if (path.lastIndexOf("/") == 0) {
// 架构师定义规范,让开发者遵循
messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解未按规范配置,如:/app/MainActivity");
return false;
}
// 从第一个 / 到第二个 / 中间截取,如:/app/MainActivity 截取出 app,order,personal 作为group
String finalGroup = path.substring(1, path.indexOf("/", 1));
// app,order,personal == options
// @ARouter注解中的group有赋值情况
if (!ProcessorUtils.isEmpty(group) && !group.equals(options)) {
// 架构师定义规范,让开发者遵循
messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的group值必须和子模块名一致!");
return false;
} else {
bean.setGroup(finalGroup);
}
// 如果真的返回ture RouterBean.group xxxxx 赋值成功 没有问题
return true;
}
}
ProcessorConfig
public interface ProcessorConfig {
// @ARouter注解 的 包名 + 类名
String AROUTER_PACKAGE = "com.xiangxue.arouter_annotation.ARouter";
// 接收参数的TAG标记
String OPTIONS = "moduleName"; // 同学们:目的是接收 每个module名称
String APT_PACKAGE = "packageNameForAPT"; // 同学们:目的是接收 包名(APT 存放的包名)
// String全类名
public static final String STRING_PACKAGE = "java.lang.String";
// Activity全类名
public static final String ACTIVITY_PACKAGE = "android.app.Activity";
// ARouter api 包名
String AROUTER_API_PACKAGE = "com.xiangxue.arouter_api";
// ARouter api 的 ARouterGroup 高层标准
String AROUTER_API_GROUP = AROUTER_API_PACKAGE + ".ARouterGroup";
// ARouter api 的 ARouterPath 高层标准
String AROUTER_API_PATH = AROUTER_API_PACKAGE + ".ARouterPath";
// 路由组,中的 Path 里面的 方法名
String PATH_METHOD_NAME = "getPathMap";
// 路由组,中的 Group 里面的 方法名
String GROUP_METHOD_NAME = "getGroupMap";
// 路由组,中的 Path 里面 的 变量名 1
String PATH_VAR1 = "pathMap";
// 路由组,中的 Group 里面 的 变量名 1
String GROUP_VAR1 = "groupMap";
// 路由组,PATH 最终要生成的 文件名
String PATH_FILE_NAME = "ARouter$$Path$$";
// 路由组,GROUP 最终要生成的 文件名
String GROUP_FILE_NAME = "ARouter$$Group$$";
}
ProcessorUtils
import java.util.Collection;
import java.util.Map;
/**
* 字符串、集合判空工具
*/
public final class ProcessorUtils {
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
public static boolean isEmpty(Collection<?> coll) {
return coll == null || coll.isEmpty();
}
public static boolean isEmpty(final Map<?, ?> map) {
return map == null || map.isEmpty();
}
}
3.Group生成管理
4.通信传输
5.动态生成懒加载参数接收
1.arouter_annotation 添加注解Parameter
/**
* <strong>Activity使用的布局文件注解</strong>
* <ul>
* <li>@Target(ElementType.TYPE) // 接口、类、枚举、注解</li>
* <li>@Target(ElementType.FIELD) // 属性、枚举的常量</li>
* <li>@Target(ElementType.METHOD) // 方法</li>
* <li>@Target(ElementType.PARAMETER) // 方法参数</li>
* <li>@Target(ElementType.CONSTRUCTOR) // 构造函数</li>
* <li>@Target(ElementType.LOCAL_VARIABLE)// 局部变量</li>
* <li>@Target(ElementType.ANNOTATION_TYPE)// 该注解使用在另一个注解上</li>
* <li>@Target(ElementType.PACKAGE) // 包</li>
* <li>@Retention(RetentionPolicy.RUNTIME) <br>注解会在class字节码文件中存在,jvm加载时可以通过反射获取到该注解的内容</li>
* </ul>
*
* 生命周期:SOURCE < CLASS < RUNTIME
* 1、一般如果需要在运行时去动态获取注解信息,用RUNTIME注解
* 2、要在编译时进行一些预处理操作,如ButterKnife,用CLASS注解。注解会在class文件中存在,但是在运行时会被丢弃
* 3、做一些检查性的操作,如@Override,用SOURCE源码注解。注解仅存在源码级别,在编译的时候丢弃该注解
*/
@Target(ElementType.FIELD) // 该注解作用在类之上 字段上有作用
@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作,注解会在class文件中存在
public @interface Parameter {
// 不填写name的注解值表示该属性名就是key,填写了就用注解值作为key
// 从getIntent()方法中获取传递参数值
String name() default "";
}
2.arouter_api ParameterGet标准规则的定义
public interface ParameterGet {
/**
* 目标对象.属性名 = getIntent().属性类型... 完成赋值操作
* @param targetParameter 目标对象:例如:MainActivity 中的那些属性
*/
void getParameter(Object targetParameter);
}
3.arouter_compiler ParameterProcessor注解处理器生成刚刚分析的模板代码
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.xiangxue.arouter_annotation.Parameter;
import com.xiangxue.arouter_compiler.utils.ProcessorConfig;
import com.xiangxue.arouter_compiler.utils.ProcessorUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
@AutoService(Processor.class) // 开启
@SupportedAnnotationTypes({ProcessorConfig.PARAMETER_PACKAGE}) // 我们服务的注解
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 同学们:这个是必填的哦
public class ParameterProcessor extends AbstractProcessor {
private Elements elementUtils; // 类信息
private Types typeUtils; // 具体类型
private Messager messager; // 日志
private Filer filer; // 生成器
// 临时map存储,用来存放被@Parameter注解的属性集合,生成类文件时遍历
// key:类节点, value:被@Parameter注解的属性集合
private Map<TypeElement, List<Element>> tempParameterMap = new HashMap<>();
@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) {
// 由于返回了 false 就不需要一下代码了
/*if (set.isEmpty()) {
messager.printMessage(Diagnostic.Kind.NOTE, "并没有发现 被@ARouter注解的地方呀");
return false; // 没有机会处理
}*/
// 扫描的时候,看那些地方使用到了@Parameter注解
if (!ProcessorUtils.isEmpty(set)) {
// 获取所有被 @Parameter 注解的 元素(属性)集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Parameter.class);
if (!ProcessorUtils.isEmpty(elements)) {
// TODO 给仓库 存储相关信息
for (Element element : elements) { // element == name, sex, age
// 字段节点的上一个节点 类节点==Key
// 注解在属性的上面,属性节点父节点 是 类节点
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// enclosingElement == Personal_MainActivity == key
if (tempParameterMap.containsKey(enclosingElement)) {
tempParameterMap.get(enclosingElement).add(element);
} else { // 没有key Personal_MainActivity
List<Element> fields = new ArrayList<>();
fields.add(element);
tempParameterMap.put(enclosingElement, fields); // 加入缓存
}
} // for end 缓存 tempParameterMap有值了
// TODO 生成类文件
// 判断是否有需要生成的类文件
if (ProcessorUtils.isEmpty(tempParameterMap)) return true;
TypeElement activityType = elementUtils.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE);
TypeElement parameterType = elementUtils.getTypeElement(ProcessorConfig.AROUTER_AIP_PARAMETER_GET);
// 生成方法
// Object targetParameter
ParameterSpec parameterSpec = ParameterSpec.builder(TypeName.OBJECT, ProcessorConfig.PARAMETER_NAME).build();
// 循环遍历 缓存tempParameterMap
// 可能很多地方都使用了 @Parameter注解,那么就需要去遍历 仓库
for (Map.Entry<TypeElement, List<Element>> entry : tempParameterMap.entrySet()) {
// key: Personal_MainActivity
// value: [name,sex,age]
TypeElement typeElement = entry.getKey();
// 非Activity直接报错
// 如果类名的类型和Activity类型不匹配
if (!typeUtils.isSubtype(typeElement.asType(), activityType.asType())) {
throw new RuntimeException("@Parameter注解目前仅限用于Activity类之上");
}
// 是Activity
// 获取类名 == Personal_MainActivity
ClassName className = ClassName.get(typeElement);
// 方法生成成功
ParameterFactory factory = new ParameterFactory.Builder(parameterSpec)
.setMessager(messager)
.setClassName(className)
.build();
// Personal_MainActivity t = (Personal_MainActivity) targetParameter;
factory.addFirstStatement();
// 难点 多行
for (Element element : entry.getValue()) {
factory.buildStatement(element);
}
// 最终生成的类文件名(类名$$Parameter) 例如:Personal_MainActivity$$Parameter
String finalClassName = typeElement.getSimpleName() + ProcessorConfig.PARAMETER_FILE_NAME;
messager.printMessage(Diagnostic.Kind.NOTE, "APT生成获取参数类文件:" +
className.packageName() + "." + finalClassName);
// 开始生成文件,例如:PersonalMainActivity$$Parameter
try {
JavaFile.builder(className.packageName(), // 包名
TypeSpec.classBuilder(finalClassName) // 类名
.addSuperinterface(ClassName.get(parameterType)) // implements ParameterGet 实现ParameterLoad接口
.addModifiers(Modifier.PUBLIC) // public修饰符
.addMethod(factory.build()) // 方法的构建(方法参数 + 方法体)
.build()) // 类构建完成
.build() // JavaFile构建完成
.writeTo(filer); // 文件生成器开始生成类文件
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// 1 2 3节课 true 执行两次 为了防止第二有问题 加了if (set.isEmpty()) { 内部机制回来检测一遍 所以有了第二次
// 4节课 false 执行一次
return false; // 前几年研究 好像记得 执行一次
}
}
4.arouter_api ParameterManager参数管理器的编写
import android.app.Activity;
import android.util.LruCache;
/**
* 参数的 加载管理器
* TODO 这是用于接收参数的
*
* 第一步:查找 Personal_MainActivity$$Parameter
* 第二步:使用 Personal_MainActivity$$Parameter this 给他
*/
public class ParameterManager {
private static ParameterManager instance;
// private boolean isCallback;
public static ParameterManager getInstance() {
if (instance == null) {
synchronized (ParameterManager.class) {
if (instance == null) {
instance = new ParameterManager();
}
}
}
return instance;
}
// LRU缓存 key=类名 value=参数加载接口
private LruCache<String, ParameterGet> cache;
private ParameterManager() {
cache = new LruCache<>(100);
}
// 为什么还要拼接,此次拼接 是为了寻找他
static final String FILE_SUFFIX_NAME = "$$Parameter"; // 为了这个效果:Order_MainActivity + $$Parameter
// 使用者 只需要调用这一个方法,就可以进行参数的接收
public void loadParameter(Activity activity) { // 必须拿到 Personal_MainActivity
String className = activity.getClass().getName(); // className == Personal_MainActivity
ParameterGet parameterLoad = cache.get(className); // 先从缓存里面拿 如果有 如果没有
if (null == parameterLoad) { // 缓存里面没东东 提高性能
// 拼接 如:Order_MainActivity + $$Parameter
// 类加载Order_MainActivity + $$Parameter
try {
// 类加载Personal_MainActivity + $$Parameter
Class<?> aClass = Class.forName(className + FILE_SUFFIX_NAME);
// 用接口parameterLoad = 接口的实现类Personal_MainActivity
parameterLoad = (ParameterGet) aClass.newInstance();
cache.put(className, parameterLoad); // 保存到缓存
} catch (Exception e) {
e.printStackTrace();
}
}
parameterLoad.getParameter(activity); // 最终的执行 会执行我们生成的类
}
}
5.使用 先暂停,后面再使用 参数接受功能 等下面的RouterManager合成
6. 组件化之路由管理器编写
1.arouter_api RouterManager的编写
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
import androidx.annotation.RequiresApi;
import com.xiangxue.arouter_annotation.bean.RouterBean;
/**
* 整个目标
* 第一步:查找 ARouter$$Group$$personal ---> ARouter$$Path$$personal
* 第二步:使用 ARouter$$Group$$personal ---> ARouter$$Path$$personal
*/
public class RouterManager {
private String group; // 路由的组名 app,order,personal ...
private String path; // 路由的路径 例如:/order/Order_MainActivity
/**
* 上面定义的两个成员变量意义:
* 1.拿到ARouter$$Group$$personal 根据组名 拿到 ARouter$$Path$$personal
* 2.操作路径,通过路径 拿到 Personal_MainActivity.class,就可以实现跳转了
*/
// 单例模式
private static RouterManager instance;
public static RouterManager getInstance() {
if (instance == null) {
synchronized (RouterManager.class) {
if (instance == null) {
instance = new RouterManager();
}
}
}
return instance;
}
// 提供性能 LRU缓存
private LruCache<String, ARouterGroup> groupLruCache;
private LruCache<String, ARouterPath> pathLruCache;
// 为了拼接,例如:ARouter$$Group$$personal
private final static String FILE_GROUP_NAME = "ARouter$$Group$$";
private RouterManager() {
groupLruCache = new LruCache<>(100);
pathLruCache = new LruCache<>(100);
}
/***
* @param path 例如:/order/Order_MainActivity
* * @return
*/
public BundleManager build(String path) {
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new IllegalArgumentException("不按常理出牌 path乱搞的啊,正确写法:如 /order/Order_MainActivity");
}
// 同学可以自己增加
// ...
if (path.lastIndexOf("/") == 0) { // 只写了一个 /
throw new IllegalArgumentException("不按常理出牌 path乱搞的啊,正确写法:如 /order/Order_MainActivity");
}
// 截取组名 /order/Order_MainActivity finalGroup=order
String finalGroup = path.substring(1, path.indexOf("/", 1)); // finalGroup = order
if (TextUtils.isEmpty(finalGroup)) {
throw new IllegalArgumentException("不按常理出牌 path乱搞的啊,正确写法:如 /order/Order_MainActivity");
}
// 证明没有问题,没有抛出异常
this.path = path; // 最终的效果:如 /order/Order_MainActivity
this.group = finalGroup; // 例如:order,personal
// TODO 走到这里后 grooup 和 path 没有任何问题 app,order,personal /app/MainActivity
return new BundleManager(); // Builder设计模式 之前是写里面的, 现在写外面吧
}
// 真正的导航
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public Object navigation(Context context, BundleManager bundleManager) {
// 例如:寻找 ARouter$$Group$$personal 寻址 ARouter$$Group$$order ARouter$$Group$$app
String groupClassName = context.getPackageName() + "." + FILE_GROUP_NAME + group;
Log.e("derry >>>", "navigation: groupClassName=" + groupClassName);
try {
// TODO 第一步 读取路由组Group类文件
ARouterGroup loadGroup = groupLruCache.get(group);
if (null == loadGroup) { // 缓存里面没有东东
// 加载APT路由组Group类文件 例如:ARouter$$Group$$order
Class<?> aClass = Class.forName(groupClassName);
// 初始化类文件
loadGroup = (ARouterGroup) aClass.newInstance();
// 保存到缓存
groupLruCache.put(group, loadGroup);
}
if (loadGroup.getGroupMap().isEmpty()) {
throw new RuntimeException("路由表Group报废了..."); // Group这个类 加载失败
}
// TODO 第二步 读取路由Path类文件
ARouterPath loadPath = pathLruCache.get(path);
if (null == loadPath) { // 缓存里面没有东东 Path
// 1.invoke loadGroup
// 2.Map<String, Class<? extends ARouterLoadPath>>
Class<? extends ARouterPath> clazz = loadGroup.getGroupMap().get(group);
// 3.从map里面获取 ARouter$$Path$$personal.class
loadPath = clazz.newInstance();
// 保存到缓存
pathLruCache.put(path, loadPath);
}
// TODO 第三步 跳转
if (loadPath != null) { // 健壮
if (loadPath.getPathMap().isEmpty()) { // pathMap.get("key") == null
throw new RuntimeException("路由表Path报废了...");
}
// 最后才执行操作
RouterBean routerBean = loadPath.getPathMap().get(path);
if (routerBean != null) {
switch (routerBean.getTypeEnum()) {
case ACTIVITY:
Intent intent = new Intent(context, routerBean.getMyClass()); // 例如:getClazz == Order_MainActivity.class
intent.putExtras(bundleManager.getBundle()); // 携带参数
context.startActivity(intent, bundleManager.getBundle());
break;
//同学们可以自己扩展 类型
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2.arouter_api BundleManager的编写
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
/**
* 跳转时 ,用于参数的传递
*/
public class BundleManager {
// Intent传输 携带的值,保存到这里
private Bundle bundle = new Bundle();
public Bundle getBundle() {
return this.bundle;
}
// 对外界提供,可以携带参数的方法
public BundleManager withString(@NonNull String key, @Nullable String value) {
bundle.putString(key, value);
return this; // 链式调用效果 模仿开源框架
}
public BundleManager withBoolean(@NonNull String key, @Nullable boolean value) {
bundle.putBoolean(key, value);
return this;
}
public BundleManager withInt(@NonNull String key, @Nullable int value) {
bundle.putInt(key, value);
return this;
}
public BundleManager withBundle(Bundle bundle) {
this.bundle = bundle;
return this;
}
// Derry只写到了这里,同学们可以自己增加 ...
// 直接完成跳转
public Object navigation(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return RouterManager.getInstance().navigation(context, this);
}
return null;
}
}
3.演示路由通信
RouterManager.getInstance()
.build("/personal/Personal_MainActivity")
.withString("name", "李元霸")
.withString("sex", "男")
.withInt("age", 99)
.navigation(this);