组件化路由架构设计图:
思考:
1:为什么要组名,为什么不直接通过path路径来取Class类对象呢
例如:组名app 组名order 组名personal
比如组名有:
app :那么对应的路径的详细的Class类文件,那么这个文件就是ARouter$Path$$app,都是APT生成的,是一对一的关系
而组对应的详细路径列表中又是一对多 ,比如 MainActivity1,MainActivity2....就是详细的路径作为Key,而value就是详细的类 对 象,是一对多的关系
我们可以模拟一个这样的场景,当用户进入首页,什么都没有做,点错了,然后点退出,然后在app的子模块中有50个Activity,在order的子模块汇总有30个Acticity,我们就有80个Activity需要加载到内存中来,如果我们进入首页只给他加载5个Activity就足够了呢?
所以我们分组的目的就是节省内存,提高性能。
2:生成这些文件干嘛用?
这些文件我们可以通过path的一个路径得到一个Class的类对象,最终完成交互。
那我们从之前(上篇文章)的一个module到现在的N个module是不是一样的呢?
是一样的,只不过我们传的path,从里面截取出来一个组名,然后通过组名找到对应的Class文件,也就是ARout$$Group$$app,r然后再拿完整的path路径去匹配具体的类文件。
然后完成子模块间的交互
之前我们建立过PathBean对象,有两个属性,一个是String 类型的path 一个是Class类型的 类文件
这个RouterBean就是把它拓展了,比如
group是从path中截取的。
接口的方式更加容易和好拓展。
有5个方面
1:就是上面的两个接口
2:公共基础库,比如我们前面有app ,order ,personal 都引用了这个库,所以只要我们在其中穿插进ARouter API,中间就可以做中转的作用。
4:使用APT生成的代码完成交互
五:撸代码:
1:按照上文的步骤,我们先创建一个arouter_api
/**
* 路由组Group加载数据接口
*/
public interface ARouterLoadGroup {
/**
* 加载路由组Group数据
* 比如:"app", ARouter$$Path$$app.class(实现了ARouterLoadPath接口)
*
* @return key:"app", value:"app"分组对应的路由详细对象类
*/
Map> loadGroup();
}
/**
* 路由组Group对应的详细Path加载数据接口
* 比如:app分组对应有哪些类需要加载
*/
public interface ARouterLoadPath {
/**
* 加载路由组Group中的Path详细数据
* 比如:"app"分组下有这些信息:
*
* @return key:"/app/MainActivity", value:MainActivity信息封装到RouterBean对象中
*/
Map loadPath();
}
首先创建自定义注解和注解处理器
通过构建者模式创建实体封装类
/**
* 路由路径Path的最终实体封装类
* 比如:app分组中的MainActivity对象,这个对象有更多的属性
*/
public class RouterBean {
public enum Type {
ACTIVITY
}
// 枚举类型:Activity
private Type type;
// 类节点
private Element element;
// 注解使用的类对象
private Class> clazz;
// 路由地址
private String path;
// 路由组
private String group;
private RouterBean(Builder builder) {
this.type = builder.type;
this.element = builder.element;
this.clazz = builder.clazz;
this.path = builder.path;
this.group = builder.group;
}
private RouterBean(Type type, Class> clazz, String path, String group) {
this.type = type;
this.clazz = clazz;
this.path = path;
this.group = group;
}
// 对外提供简易版构造方法,主要是为了方便APT生成代码
public static RouterBean create(Type type, Class> clazz, String path, String group) {
return new RouterBean(type, clazz, path, group);
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public Element getElement() {
return element;
}
public void setElement(Element element) {
this.element = element;
}
public Class> getClazz() {
return clazz;
}
public void setClazz(Class> clazz) {
this.clazz = clazz;
}
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;
}
/**
* 构建者模式
*/
public static class Builder {
// 枚举类型:Activity
private Type type;
// 类节点
private Element element;
// 注解使用的类对象
private Class> clazz;
// 路由地址
private String path;
// 路由组
private String group;
public Builder setType(Type type) {
this.type = type;
return this;
}
public Builder setElement(Element element) {
this.element = element;
return this;
}
public Builder setClazz(Class> clazz) {
this.clazz = clazz;
return this;
}
public Builder setPath(String path) {
this.path = path;
return this;
}
public Builder setGroup(String group) {
this.group = group;
return this;
}
// 最后的build或者create,往往是做参数的校验或者初始化赋值工作
public RouterBean build() {
if (path == null || path.length() == 0) {
throw new IllegalArgumentException("path必填项为空,如:/app/MainActivity");
}
return new RouterBean(this);
}
}
@Override
public String toString() {
return "RouterBean{" +
"path='" + path + '\'' +
", group='" + group + '\'' +
'}';
}
}
@Target(ElementType.TYPE) // 该注解作用在类之上
@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作,注解会在class文件中存在
public @interface ARouter {
// 详细路由路径(必填),如:"/app/MainActivity"
String path();
// 路由组名(选填,如果开发者不填写,可以从path中截取出来)
String group() default "";
}
/**
* 常量类
*/
public class Constants {
// 注解处理器中支持的注解类型
public static final String IROUTER_ANNOTATION_TYPES = "com.netease.arouter.annotation.ARouter";
}
/**
* 编码此类1句话:细心再细心,出了问题debug真的不好调试
*/
// AutoService则是固定的写法,加个注解即可
// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册
// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)
// 允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({Constants.IROUTER_ANNOTATION_TYPES})
// 指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ARouterProcessor extends AbstractProcessor {
// 操作Element工具类 (类、函数、属性都是Element)
private Elements elementUtils;
// type(类信息)工具类,包含用于操作TypeMirror的工具方法
private Types typeUtils;
// Messager用来报告错误,警告和其他提示信息
private Messager messager;
// 文件生成器 类/资源,Filter用来创建新的源文件,class文件以及辅助文件
private Filer filer;
// 该方法主要用于一些初始化的操作,通过该方法的参数ProcessingEnvironment可以获取一些列有用的工具类
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
}
/**
* 相当于main函数,开始处理注解
* 注解处理器的核心方法,处理具体的注解,生成Java文件
*
* @param set 使用了支持处理注解的节点集合
* @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找的注解。
* @return true 表示后续处理器不会再处理(已经处理完成)
*/
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
但是我们看到这progress里面没有处理,我们需要使用ARouter_api里面的加载数据接口。
// 每个功能子模块既然都要生成APT源文件,而且又是所有模块依赖的公共库
// 那么公共基础库依赖路由arouter_api就能向每个子模块提供开放api了
所以在公共库的build.gradle中需要依赖arouter_api
api project(':arouter_api') // 路由对外开放api模块
所以将处理逻辑放到common中。
我们模拟一个想要的路由器组文件
先从传入的路径中截取group信息 返回的是 Map
/**
* 模拟ARouter路由器的组文件
*/
public class ARouter$$Group$$order implements ARouterLoadGroup {
@Override
public Map> loadGroup() {
Map> groupMap = new HashMap<>();
groupMap.put("order", ARouter$$Path$$order.class);
return groupMap;
}
}
//我们通过路由组中的path详细数据,可以得到RouterBean对象,里面包含group,path,Class对象等信息
/**
* 路由组Group对应的详细Path加载数据接口
* 比如:app分组对应有哪些类需要加载
*/
public interface ARouterLoadPath {
/**
* 加载路由组Group中的Path详细数据
* 比如:"app"分组下有这些信息:
*
* @return key:"/app/MainActivity", value:MainActivity信息封装到RouterBean对象中
*/
Map loadPath();
}
我们在common建立一个管理类,用来将Activity添加到两个集合中:group集合,path集合
/**
* 全局路径记录器(根据子模块分组)
*/
public class RecordPathManager {
// key:"order"组 value:order子模块下,对应所有的Activity路径信息
private static Map> groupMap = new HashMap<>();
/**
* 将路径信息加入全局Map
*
* @param groupName 组名,如:"personal"
* @param pathName 路劲名,如:"Personal_MainActivity"
* @param clazz 类对象,如:Personal_MainActivity.class
*/
public static void joinGroup(String groupName, String pathName, Class> clazz) {
List list = groupMap.get(groupName);
if (list == null) {
list = new ArrayList<>();
list.add(new PathBean(pathName, clazz));
groupMap.put(groupName, list);
} else {
groupMap.put(groupName, list);
}
groupMap.put(groupName, list);
}
/**
* 根据组名和路径名获取类对象,达到跳转目的
*
* @param groupName 组名
* @param pathName 路径名
* @return 跳转目标的class类对象
*/
public static Class> getTargetClass(String groupName, String pathName) {
List list = groupMap.get(groupName);
if (list == null) return null;
for (PathBean path : list) {
if (pathName.equalsIgnoreCase(path.getPath())) {
return path.getClazz();
}
}
return null;
}
/**
* 清理、回收
*/
public static void recycleGroup() {
groupMap.clear();
groupMap = null;
System.gc();
}
}
在哪里调用呢?
在app中:
public class AppApplication extends BaseApplication {
@Override
public void onCreate() {
super.onCreate();
// 如果项目有100个Activity,这种加法会不会太那个?
RecordPathManager.joinGroup("app", "MainActivity", MainActivity.class);
RecordPathManager.joinGroup("order", "Order_MainActivity", Order_MainActivity.class);
RecordPathManager.joinGroup("personal", "Personal_MainActivity", Personal_MainActivity.class);
}
}