随着业务的发展,项目会越来越大,实现组件化便于更好的维护项目以及拆分。
业务组件相互独立,需要路由来完成之间的跳转与数据传递。
实现方法:
1、 使用 EventBus的方式,缺点是:EventBean维护成本太高,不好去管理:
2、 使用广播的方式,缺点是:不好管理,都统一发出去了
3、 使用隐士意图方式,缺点是:在AndroidManifest.xml里面配置xml写的太多了
4、 使用类加载方式,缺点就是,容易写错包名类名,缺点较少
5、 使用全局Map的方式,缺点是,要注册很多的对象
6、事件总线的思路。(可以参考CC)
1~5这些不好维护,我们可以利用APT、JavaPoet技术参考阿里的ARouter、ButterKnife等思路来完成自己的一个Router路由框架,先了解一下APT以及JavaPoet。
APT: annotation process tool是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。
APT 编译的时候 ----------> 处理注解 --------> 生成代码----------> 编译完成
JavaPoet: JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件,是Java面向对象OOP语法 ,可以很方便的使用它根据注解生成对应代码,通过这种自动化生成代码的方式, 可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。
传统的APT : package ---> class ---> method
JavaPoet: method---->class----->package
我们看一下我们项目的框架
image.png
我们类跳转的时候,先找到group,在找到对应的path就可以跳转的目标了。我们可以用两个Map来实现group和path映射。先看一下效果:
group
public class ARouter$$Group$$user implements ARouterGroup {
@Override
public Map<String, Class<? extends ARouterPath>> getGroupMap() {
Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
groupMap.put("user",ARouter$$Path$$user.class);
return groupMap;
}
}
path
public class ARouter$$Path$$user implements ARouterPath {
@Override
public Map<String, RouterBean> getPathMap() {
Map<String, RouterBean> pathMap = new HashMap<>();
pathMap.put("/user/User_LoginAcitivty", RouterBean.create(RouterBean.TypeEnum.ACTIVITY,User_LoginAcitivty.class,"/user/User_LoginAcitivty","user"));
pathMap.put("/user/User_MainActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY,User_MainActivity.class,"/user/User_MainActivity","user"));
return pathMap;
}
}
1、我们可以仿照ARouter的架构来搭建自己的框架
image.png
arouter-annotation、arouter-compiler为java-library。把注解列存放在arouter-annotation包中。(注解不清楚的先了解下)
image.png
arouter-compiler的gradle要依赖
image.png
。
ArouterPath接口
public interface ARouterPath {
/**
* 存放的内容key:/app/MainActivity value:RouterBean == App_MainActivity.class
*/
Map<String, RouterBean> getPathMap();
}
RouterBean用于封装app、user等组件中的MainActivity等对象, 封装成对象会有更多的属性
public class RouterBean {
public enum TypeEnum {//可扩展 fragment
ACTIVITY
}
private TypeEnum typeEnum;//枚举类型activity
private Element element;//节点 JavaPoet,可以拿到很多的信息
private Class<?> myClass;// 被注解 class对象 eg: MainActivity.class
private String path;// 路由地址 eg: /app/MainActivity
private String group;// 路由组 eg: app user等
public TypeEnum getTypeEnum() {
return typeEnum;
}
public void setTypeEnum(TypeEnum typeEnum) {
this.typeEnum = typeEnum;
}
public Element getElement() {
return element;
}
public void setElement(Element element) {
this.element = element;
}
public Class<?> getMyClass() {
return myClass;
}
public void setMyClass(Class<?> myClass) {
this.myClass = myClass;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
private RouterBean(TypeEnum typeEnum, Class<?> myClass, String path, String group) {
this.typeEnum = typeEnum;
// this.element = element;
this.myClass = myClass;
this.path = path;
this.group = group;
}
//对外暴露 为了方便APT生成代码
public static RouterBean create(TypeEnum typeEnum, Class<?> clazz,String path, String group) {
return new RouterBean(typeEnum, clazz, path, group);
}
/**
* 构建者模式
*/
public static class Builder {
// 枚举类型:Activity
private TypeEnum type;
// 类节点
private Element element;
// 注解使用的类对象
private Class<?> clazz;
// 路由地址
private String path;
// 路由组
private String group;
public Builder addType(TypeEnum type) {
this.type = type;
return this;
}
public Builder addElement(Element element) {
this.element = element;
return this;
}
public Builder addClazz(Class<?> clazz) {
this.clazz = clazz;
return this;
}
public Builder addPath(String path) {
this.path = path;
return this;
}
public Builder addGroup(String group) {
this.group = group;
return this;
}
public RouterBean build() {
if (path == null || path.length() == 0) {
throw new IllegalArgumentException("path路径为空,请检查" + clazz);
}
return new RouterBean(this);
}
}
private RouterBean(Builder builder) {
this.typeEnum = builder.type;
this.element = builder.element;
this.myClass = builder.clazz;
this.path = builder.path;
this.group = builder.group;
}
@Override
public String toString() {
return "RouterBean{" +
"path='" + path + '\'' +
", group='" + group + '\'' +
'}';
}
}
public interface ARouterGroup {
/**
*
* 存放的内容key:app value:app所有的path类
*/
Map<String, Class<? extends ARouterPath>> getGroupMap();
}
工具类
package com.htf.arouter_compiler.util;
public interface ProcessorConfig {
// @ARouter注解 的 包名 + 类名
String AROUTER_PACKAGE = "com.htf.arouter_annotation.ARouter";
//
String PARAMETER_PACKAGE = "com.htf.arouter_annotation.Parameter";
// 接收module参数
String OPTIONS = "moduleName";
// 目的是接收 包名
String APT_PACKAGE = "packageNameForAPT";
// 为了匹配加注解的类是否为Activity
public static final String ACTIVITY_PACKAGE = "android.app.Activity";
// ARouter api 包名
String AROUTER_API_PACKAGE = "com.htf.arouter_api";
// ARouter api 的 ARouterPath 高层标准
String AROUTER_API_PATH = AROUTER_API_PACKAGE + ".ARouterPath";
// ARouter api 的 ARouterGroup 高层标准
String AROUTER_API_GROUP = AROUTER_API_PACKAGE + ".ARouterGroup";
// 路由组,中的 Path 里面的 方法名
String PATH_METHOD_NAME = "getPathMap";
// 路由组,中的 Path 里面 的 变量名
String PATH_VAR1 = "pathMap";
// 路由组,PATH 最终要生成的 文件名
String PATH_FILE_NAME = "ARouter$$Path$$";
// 路由组,中的 Group 里面的 方法名
String GROUP_METHOD_NAME = "getGroupMap";
// 路由组,中的 Group 里面 的 变量名 1
String GROUP_VAR1 = "groupMap";
// 路由组,GROUP 最终要生成的 文件名
String GROUP_FILE_NAME = "ARouter$$Group$$";
// ARouter api 的 ParameterGet 高层标准
String AROUTER_AIP_PARAMETER_DATA = AROUTER_API_PACKAGE + ".ParameterData";
// ARouter api 的 ParmeterGet 方法的名字
String PARAMETER_METHOD_NAME = "getParameter";
// ARouter api 的 ParameterGet 方法参数的名字
String PARAMETER_NAME = "targetParameter";
// String全类名
String STRING = "java.lang.String";
//用户判断是否是自定义对象类型 要实现Parcelable
String PARCELABLE = "android.os.Parcelable";
// ARouter aip 的 ParmeterGet 的 生成文件名称 $$Parameter
String PARAMETER_FILE_NAME = "$$Parameter";
//用户判断是否是List类型
String LIST_STRING = "java.util.List<java.lang.String>";
String LIST_PARCELABLE = "java.util.List<&T>";
}
利用APT生成ARouter$$Path$$user
和ARouter$$Group$$user
文件
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.htf.arouter_annotation.ARouter")//自己的注解类
@SupportedOptions({"moduleName", "packageNameForAPT"})// moduleName接收module参数;packageNameForAPT目的是接收 包名
public class ARouterProcessor extends AbstractProcessor {
private Types typeTool;//// type(类信息)的工具类,包含用于操作TypeMirror的工具方法
private Elements elementTool;// 操作Element的工具类(类,函数,属性,其实都是Element)
private Messager messager;
private Filer filer;
private String options; // 各个模块传递过来的模块名 例如:app user zixun order等
private String aptPackage; // 各个模块传递过来的目录 用于统一存放 apt生成的文件
private Map<String, List<RouterBean>> mAllPathMap = new HashMap<>();
// Map<"user", "ARouter$$Path$$user.class">
private Map<String, String> mAllGroupMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementTool = processingEnvironment.getElementUtils();
filer = processingEnvironment.getFiler();
messager = processingEnvironment.getMessager();
typeTool = processingEnvironment.getTypeUtils();
options = processingEnvironment.getOptions().get(ProcessorConfig.OPTIONS);
aptPackage = processingEnvironment.getOptions().get(ProcessorConfig.APT_PACKAGE);
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set.isEmpty()) {
messager.printMessage(Diagnostic.Kind.NOTE, "没有发现注解的类");
return false;
}
// // 获取所有被 @ARouter 注解的 元素集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
// 通过Element工具类,获取android.app.Activity类型
TypeElement activityElement = elementTool.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE);
TypeMirror activityMirror = activityElement.asType();
for (Element element : elements) {
String className = element.getSimpleName().toString();
ARouter aRouter = element.getAnnotation(ARouter.class);
messager.printMessage(Diagnostic.Kind.NOTE, "aRouter.group() == " + aRouter.group());
RouterBean routerBean = new RouterBean.Builder()
.addElement(element)
.addGroup(options)
.addPath(aRouter.path())
.build();
TypeMirror myMirror = element.asType();//自己类的TypeMirror 用于判断是不是继承android.app.Activity
if (typeTool.isSubtype(myMirror, activityMirror)) {//是activity
routerBean.setTypeEnum(RouterBean.TypeEnum.ACTIVITY);
} else {
throw new RuntimeException(className + "必须是Activity");
}
//把所有的注解类封以module名为key封装到mAllPathMap中
if (checkRouterPath(routerBean)) {
List<RouterBean> routerBeanList = mAllPathMap.get(routerBean.getGroup());
if (ProcessorUtils.isEmpty(routerBeanList)) {
routerBeanList = new ArrayList<>();
routerBeanList.add(routerBean);
mAllPathMap.put(routerBean.getGroup(), routerBeanList);
} else {
routerBeanList.add(routerBean);
}
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解未按规范配置,如:/app/MainActivity");
}
}//循环结束,把所有的注解类以module名为key封装到mAllPathMap
// TODO 开始用javapoet生成文件
//定义文件要实现的接口
TypeElement pathTypeElement = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_PATH);
try {
createPathFile(pathTypeElement);
} catch (IOException e) {
e.printStackTrace();
}
TypeElement groupType = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_GROUP);
try {
createGroupFile(groupType, pathTypeElement);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
//生成路由组Group文件,如:ARouter$$Group$$app
private void createGroupFile(TypeElement groupType, TypeElement pathType) throws IOException {
if (ProcessorUtils.isEmpty(mAllGroupMap) || ProcessorUtils.isEmpty(mAllPathMap)) return;
//要生成的内容
//Map<String, Class<? extends ARouterPath>>
TypeName methodReturn = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
//Class<? extends ARouterPath>>
ParameterizedTypeName.get(ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(pathType))) // ? extends ARouterLoadPath)
);
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.GROUP_METHOD_NAME)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(methodReturn);
//方法里的内容
// Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
methodBuilder.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("app", ARouter$$Path$$app.class);
// groupMap.put("user", ARouter$$Path$$user.class);
methodBuilder.addStatement("$N.put($S,$T.class)",
ProcessorConfig.GROUP_VAR1,
options,
ClassName.get(aptPackage, mAllGroupMap.get(options)));
methodBuilder.addStatement("return $N", ProcessorConfig.GROUP_VAR1);
///生成文件
String fileClassName = ProcessorConfig.GROUP_FILE_NAME + options;
JavaFile.builder(aptPackage,
TypeSpec.classBuilder(fileClassName)
.addSuperinterface(ClassName.get(groupType))
.addModifiers(Modifier.PUBLIC)
.addMethod(methodBuilder.build())
.build())
.build()
.writeTo(filer);
}
private void createPathFile(TypeElement typeElement) throws IOException {
//先把方法返回类型生成 Map<String, RouterBean>
ParameterizedTypeName methodReturn = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouterBean.class)
);
//生成方法
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.PATH_METHOD_NAME)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(methodReturn);
for (Map.Entry<String, List<RouterBean>> entry : mAllPathMap.entrySet()) {
////方法里的内容 Map<String, RouterBean> pathMap = new HashMap<>();
methodBuilder.addStatement("$T<$T, $T> $N = new $T<>()",
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouterBean.class),
ProcessorConfig.PATH_VAR1,
ClassName.get(HashMap.class));
List<RouterBean> routerBeanList = entry.getValue();
for (RouterBean routerBean : routerBeanList) {
methodBuilder.addStatement("$N.put($S, $T.create($T.$L, $T.class, $S, $S))",
ProcessorConfig.PATH_VAR1,
routerBean.getPath(),
ClassName.get(RouterBean.class),
ClassName.get(RouterBean.TypeEnum.class),
routerBean.getTypeEnum(),
ClassName.get((TypeElement) routerBean.getElement()),
routerBean.getPath(),
routerBean.getGroup());
}
// return pathMap;
methodBuilder.addStatement("return $N", ProcessorConfig.PATH_VAR1);
}
//生成的类文件名ARouter$$Path$$user 有implements所以方法和类要合为一体生成
String fileName = ProcessorConfig.PATH_FILE_NAME + options;
messager.printMessage(Diagnostic.Kind.NOTE, "APT生成路由Path类文件:" +
aptPackage + "." + fileName);
JavaFile.builder(aptPackage,
TypeSpec.classBuilder(fileName)
.addSuperinterface(ClassName.get(typeElement))
.addMethod(methodBuilder.build())
.addModifiers(Modifier.PUBLIC)
.build())
.build()
.writeTo(filer);
mAllGroupMap.put(options, fileName);
}
private boolean checkRouterPath(RouterBean bean) {
String group = bean.getGroup();//app、user、zixun等module
String path = bean.getPath();// /app/MainActivity
// TODO 校验path
// @ARouter注解中的path值,必须要以 / 开头
if (ProcessorUtils.isEmpty(path) || !path.startsWith("/")) {
messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的path值,必须要以 / 开头");
return false;
}
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));
if (!ProcessorUtils.isEmpty(group) && !group.equals(options)) {
messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的group值必须和子模块名一致!");
return false;
} else {
bean.setGroup(finalGroup);
}
return true;
}
}
ARouterProcessor会检测带注解,利用roundEnvironment.getElementsAnnotatedWith(ARouter.class);来判断哪些是自己ARouter注解类,然后javapoet语法生成Java文件。build一下项目。
不要忘了在类上添加注解
image.png
最后生成文件的效果
image.png
最后调用
ARouter$$Group$$user group$$user = new ARouter$$Group$$user();
Map<String, Class<? extends ARouterPath>> groupMap = group$$user .getGroupMap();
Class<? extends ARouterPath> myClass = groupMap.get("user ");
try {
ARouter$$Path$$user path = (ARouter$$Path$$user ) myClass.newInstance();
Map<String, RouterBean> pathMap = path.getPathMap();
RouterBean bean = pathMap.get("/user /User_LoginAcitivty");
if (bean != null) {
Intent intent = new Intent(this, bean.getMyClass());
startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
现在调用还复杂化、不能参数据,在第二篇文章将会再次封装,源码链接放在第二篇文章里。
网友评论